Skip to content

Routing

Paulo Gomes da Cruz Junior edited this page Nov 19, 2025 · 1 revision

🧭 Routing System

React Router is the most widely adopted routing library in the React ecosystem — and for good reason. It provides a declarative, component-based API for managing navigation, enabling seamless transitions between views without full page reloads.

📦 Installation

To install React Router in your project, run:

npm install react-router-dom

Wrap your root component with <BrowserRouter> to enable routing:

import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';

const root = document.getElementById('root');
ReactDOM.createRoot(root).render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

Overview

This project uses React Router v6 to manage client-side navigation. Routes are defined using the RouteDescription type, which supports metadata such as access control, menu visibility, and offline readiness. The routing system is dynamic, secure, and responsive to user roles and network status.

Routes are composed in two layers:

  • Core route definitions (routePaths.tsx)
  • Runtime router setup (AppRoutes.tsx)

🗂️ Route Definitions

Routes are declared using the RouteDescription type, which extends React Router's RouteObject with additional fields:

type RouteDescription = {
  id: string; // Unique identifier and display name
  path: string;
  element: React.ReactNode;
  icon?: React.ComponentType;
  protected?: boolean;
  isSideMenu: boolean;
  children?: RouteDescription[];
  roles?: string[];
  offlineReady?: boolean;
  offlineOnly?: boolean;
} & RouteObject;

Example: Top-Level Route

{
  id: 'Dashboard',
  path: '/dashboard',
  element: (
    <Layout>
      <DashboardPage />
    </Layout>
  ),
  icon: DashboardReference,
  protected: true,
  isSideMenu: true,
}

Example: Nested Routes with Parameters

{
  id: 'Entries',
  path: '/entries',
  icon: ManageProtection,
  element: (
    <Layout>
      <Outlet />
    </Layout>
  ),
  isSideMenu: true,
  protected: true,
  children: [
    {
      id: 'Entry List',
      path: '', // matches /entries
      icon: Table,
      element: <ListEntriesPage />,
      isSideMenu: true,
      protected: true,
    },
    {
      id: 'Entry Details',
      path: ':id', // matches /entries/123
      element: <DetailsPage />,
      isSideMenu: false,
      protected: true,
    },
  ],
}
  • The parent route uses <Outlet /> to render its children.
  • Child routes use relative paths ('' for the index, :id for parameters).
  • Navigating to /entries renders the list; /entries/123 renders details.

🔐 Access Control & Filtering

The routing system supports dynamic filtering based on:

  • User roles
  • Online/offline status

Key Functions

  • hasAccess(route, isOnline, roles): Determines if a route should be accessible.
  • filterRoutesRecursively(routes, isOnline, roles): Recursively filters nested routes.
  • getProtectedRoutes(isOnline, roles): Returns all accessible routes for authenticated users.
  • getPublicRoutes(): Returns routes available to unauthenticated users.
  • getMenuEntries(isOnline, roles): Extracts menu items for the side nav, including nested children.

🚦 Runtime Routing

The AppRoutes component sets up the router using createBrowserRouter and wraps it in a Suspense boundary for lazy loading:

const routesToUse = useMemo(() => {
  if (!isLoggedIn) return getPublicRoutes();
  return getProtectedRoutes(true, user?.roles || []);
}, [isLoggedIn, user?.roles]);

const browserRouter = useMemo(() => createBrowserRouter(routesToUse), [routesToUse]);

return (
  <Suspense fallback={<Loading withOverlay />}>
    <RouterProvider router={browserRouter} />
  </Suspense>
);

🧩 How to Add New Routes

  1. Define your route in ROUTES (or SYSTEM_ROUTES for system pages):

    • Use a unique id.
    • Set the path (use parameters like :id for dynamic segments).
    • Provide the element to render.
    • Optionally set icon, protected, isSideMenu, roles, etc.
    • For nested routes, add a children array.
  2. For nested routes:

    • The parent route should use <Outlet /> in its element.
    • Child routes use relative paths.
  3. Access control:

    • Set protected: true and specify roles if needed.
    • Use offlineReady or offlineOnly for offline support.
  4. Menu entries:

    • Set isSideMenu: true to include the route in the side navigation.
    • Nested menu items are supported via the children field.

� Example: Adding a New Route

{
  id: 'Settings',
  path: '/settings',
  element: <SettingsPage />,
  icon: SettingsIcon,
  protected: true,
  isSideMenu: true,
  offlineReady: true,
}

For a nested route with parameters:

{
  id: 'Profile',
  path: '/profile',
  element: <Layout><Outlet /></Layout>,
  isSideMenu: true,
  protected: true,
  children: [
    {
      id: 'Profile Details',
      path: ':userId',
      element: <ProfileDetailsPage />,
      isSideMenu: false,
      protected: true,
    },
  ],
}

� Notes

  • Use isSideMenu: false for routes like login or modal-only views.
  • The id field is used for display and identification (was previously name).
  • roles can be used to filter routes based on user permissions.
  • offlineReady and offlineOnly help tailor the UI for offline-first experiences.
  • You can extend RouteObject to include loaders, actions, or error boundaries as needed.

Clone this wiki locally