Yashveer Singh
Connect
<- All posts
Web App and Frontend Development6 min read

Routing Patterns That Survive Real World Use

Web application routing is the system that maps URLs to the components or pages that should be rendered, and manages navigation state between pages. Routing patterns determine how URLs are structured, how route parameters are typed and validated, how nested layouts are shared across routes, how loading and error states are handled during navigation, and how route guards (authentication, authorization) are applied. Good routing patterns are navigable by URL, bookmarkable, shareable, and consistent with the user's browser navigation expectations.

Written by Yashveer Singh, founder of Yashveer Labs.

What you need to know

  • URL structure should reflect the data hierarchy and be stable. URLs that change when the backend changes break bookmarks, links, and user trust.
  • Authentication guards belong at the layout level, not repeated on every page component. Use the route structure itself to define protected areas.
  • Nested routing enables shared layouts without code duplication. Use it for any section of the app with a consistent navigation structure.
  • Query string state should be synchronized with the URL for any filter, sort, or view state the user expects to survive a page refresh or share via link.
  • Routing conventions established early are hard to change later. Agree on URL structure and parameter conventions before the first feature is built.

The core argument

Routing is one of the foundational decisions in a web application that compound over time. An application with inconsistent URL structure (some pages use query strings for IDs, others use path parameters, some are nested, others are flat) accumulates navigation bugs, deeplink failures, and analytics inconsistencies that are expensive to fix after they are entrenched.

The routing decisions that matter most are the early ones. URL structure conventions (how IDs appear in paths, how filters appear in query strings, how nested routes are organized) are visible in every link, every bookmark, every analytics event, and every support ticket. Changing them after users have linked to them breaks those links. Establishing the right conventions at the start is one of the highest-leverage architectural decisions in a web application.

The pattern that works well for SaaS applications: path-based routing for resource hierarchy (/dashboard, /users/:id, /orders/:id/items), query string state for view configuration (?filter=active&sort=created_at), persistent layout components at the route group level, authentication guards at the protected route group level, and loading states handled by the route's suspense boundaries or loading components rather than scattered through individual page components.

Common mistakes

  1. Using numeric IDs in URLs without considering ID enumeration. A URL like /orders/12345 exposes that there are at least 12,345 orders and allows enumeration of sequential IDs. Use UUID or random IDs in public URLs for resources that should not be enumerable. Sequential IDs are acceptable for internal admin tools where enumeration is not a security concern.
  1. Storing authentication state in localStorage and checking it client-side only. Authentication that is only checked in client-side JavaScript allows users to bypass the check by manipulating localStorage. Authentication guards should verify the session server-side (in middleware, API routes, or getServerSideProps) in addition to any client-side redirect. Client-side guards are a UX convenience; server-side guards are the security boundary.
  1. Not preserving the destination URL after login. An authentication guard that redirects to the login page without preserving the intended destination forces users to navigate back to where they were after logging in. Preserve the intended URL in a redirect query parameter and use it after successful authentication to send the user to the right place.
  1. Using the same route for create and edit operations. Routes like /users/new and /users/:id/edit can share a form component, but they represent different operations with different data loading requirements (new has no existing data, edit must load the existing record). Route-level separation makes the data loading intent explicit and prevents subtle bugs where the edit form renders before the data loads.
  1. Not handling 404 and error routes. Every web application needs a 404 route for URLs that do not match any route, and error boundaries for routes that fail to load. Default browser 404 pages and unhandled route errors produce jarring user experiences. Implement custom 404 and error pages that match the application's design and provide navigation back to functional areas.

Where to start

  1. Define the URL structure for the core application entities before writing routes. For each major entity (users, orders, products, whatever your domain has), decide: what is the list URL, what is the detail URL, what is the edit URL, and how are filters and sort state represented. Document this as a routing convention reference before building the first route.
  1. Create route groups for protected and public areas. In Next.js App Router, route groups (folders with names in parentheses) allow applying different layouts and middleware to different sections of the application without affecting the URL structure. Create protected and public route groups from the start, even if the protected group only contains the dashboard initially.
  1. Implement loading and error states at the route level, not the component level. React Router's loaders and Next.js App Router's loading.tsx and error.tsx files handle loading and error states for entire routes. Using these route-level constructs instead of per-component loading states produces consistent loading behavior and reduces the amount of loading state management code in individual components.

Related reading

FAQ

Frequently asked

Author

The engineering bet behind Yashveer Labs

The bet I am running with Yashveer Labs is simple. Most software is built by people who treat it as a job. I treat it as a craft. Yashveer Singh, founder. Five production systems on the board so far. The arc points at machine learning, AI engineering, and cybersecurity. If your project is in any of those orbits, you are reading the right page.

Related reading