Routing

When I first built the POS frontend, everything was on one page. The product list, cart, checkout form, and order history were all rendered conditionally based on state: {view === 'products' && <ProductList />}, {view === 'cart' && <Cart />}. It worked for a demo, but users couldn't bookmark pages, the back button didn't work, and the URL was always just /. It felt broken.

Then I added React Router. Suddenly, /products showed products, /cart showed the cart, /orders/123 showed a specific order. Users could share links, use browser navigation, and the app felt like a real website. Routing transforms a single-page app into a multi-page experience.

What Is React Router?

React Router is the standard library for routing in React. It:

  • Maps URLs to components (/products β†’ <ProductList />)

  • Handles browser navigation (back/forward buttons)

  • Enables programmatic navigation (navigate('/cart'))

  • Supports URL parameters (/products/:id)

  • Provides protected routes (require authentication)

In the POS system, the routes are:

  • / - Home/Dashboard

  • /products - Product catalog

  • /products/:id - Product details

  • /cart - Shopping cart

  • /checkout - Checkout flow

  • /orders - Order history

  • /orders/:id - Order details

  • /login - Login page

Installation

For TypeScript:

Your First Routes

Let's set up basic routing for the POS.

What's happening:

  1. <BrowserRouter> wraps everything (provides routing context)

  2. <Routes> contains all route definitions

  3. <Route> maps a path to a component

  4. path="*" catches unmatched routes (404 page)

Now visiting /products renders <ProductsPage />, /cart renders <CartPage />, etc.

Don't use <a> tags for internal navigationβ€”they cause full page reloads. Use <Link> instead.

<Link to="/products"> renders an <a> tag but intercepts clicks to use client-side routingβ€”no page reload!

NavLink adds an active class to the current route's link.

Route Parameters

Dynamic routes use parameters: /products/:id matches /products/123, /products/abc, etc.

Accessing Parameters with useParams

URL: /products/abc123 β†’ id = "abc123"

Linking to Dynamic Routes

Programmatic Navigation

Sometimes you need to navigate from code (e.g., after form submission).

navigate('/orders/123') - Navigate to a route

navigate(-1) - Go back (like browser back button)

navigate(-2) - Go back 2 pages

Nested Routes

For shared layouts, use nested routes.

<Outlet /> is where child routes render.

Benefits:

  • Shared header/nav across multiple pages

  • Don't repeat layout code

  • Clean route structure

Protected Routes (Authentication)

Some routes should only be accessible to authenticated users.

Using Protected Routes

Now:

  • Unauthenticated users β†’ redirected to /login

  • Regular users can access cart/checkout/orders

  • Only admins can access /admin

Complete POS Routing Setup

Here's the production routing structure for the POS system:

Enhanced Navigation Component

Query Parameters

For filters and search, use query parameters.

URL examples:

  • /products - All products

  • /products?category=food - Food category

  • /products?search=chicken&category=food - Search + filter

Benefits:

  • Shareable URLs

  • Browser back/forward works

  • Bookmarkable filtered views

Redirects and Navigation Guards

Redirect After Login

Updated ProtectedRoute

Flow:

  1. User tries to access /orders (protected)

  2. Not authenticated β†’ redirect to /login with state

  3. User logs in

  4. Redirect back to /orders

Real POS Example: Complete Order Flow

Common Mistakes to Avoid

2. Forgetting BrowserRouter

3. Not Handling 404s

4. Hardcoding URLs

Key Learnings

  1. React Router maps URLs to components

  2. Use <Link> for navigation, not <a>

  3. useParams accesses route parameters

  4. useNavigate for programmatic navigation

  5. <Outlet /> for nested routes

  6. Protected routes require authentication

  7. Query parameters for filters/search

  8. Always handle 404s with catch-all route

Next Steps

You've built a complete multi-page POS system with routing. But as the app grows, you might notice performance issuesβ€”unnecessary re-renders, slow calculations, large bundle sizes. In the final article, we'll optimize the POS for production with useMemo, useCallback, React.memo, code splitting, and deployment strategies.

Continue to: Performance & Production β†’

Last updated