Routing & Layout

React Router v7 route definitions and the application shell layout for the MetaForge Digital Twin Dashboard. Every page is lazy-loaded for optimal bundle splitting.


src/routes/index.tsx

Central route table. Each page import is wrapped with React.lazy and rendered inside a shared Suspense boundary with a skeleton fallback.

import { lazy, Suspense } from "react";
import {
  createBrowserRouter,
  RouterProvider,
  type RouteObject,
} from "react-router";
import { Layout } from "@/routes/layout";
import { PageSkeleton } from "@/components/ui/page-skeleton";

// ---------------------------------------------------------------------------
// Lazy page imports
// ---------------------------------------------------------------------------

const DashboardPage = lazy(() => import("@/pages/dashboard"));
const SessionsPage = lazy(() => import("@/pages/sessions"));
const SessionDetailPage = lazy(() => import("@/pages/session-detail"));
const AgentsPage = lazy(() => import("@/pages/agents"));
const DigitalTwinPage = lazy(() => import("@/pages/digital-twin"));
const BOMPage = lazy(() => import("@/pages/bom"));
const CompliancePage = lazy(() => import("@/pages/compliance"));
const TestingPage = lazy(() => import("@/pages/testing"));
const SupplyChainPage = lazy(() => import("@/pages/supply-chain"));
const ApprovalsPage = lazy(() => import("@/pages/approvals"));
const SettingsPage = lazy(() => import("@/pages/settings"));

// ---------------------------------------------------------------------------
// Suspense wrapper
// ---------------------------------------------------------------------------

function Suspended({ children }: { children: React.ReactNode }) {
  return <Suspense fallback={<PageSkeleton />}>{children}</Suspense>;
}

// ---------------------------------------------------------------------------
// Route definitions
// ---------------------------------------------------------------------------

const routes: RouteObject[] = [
  {
    element: <Layout />,
    children: [
      {
        index: true,
        element: (
          <Suspended>
            <DashboardPage />
          </Suspended>
        ),
      },
      {
        path: "sessions",
        element: (
          <Suspended>
            <SessionsPage />
          </Suspended>
        ),
      },
      {
        path: "sessions/:id",
        element: (
          <Suspended>
            <SessionDetailPage />
          </Suspended>
        ),
      },
      {
        path: "agents",
        element: (
          <Suspended>
            <AgentsPage />
          </Suspended>
        ),
      },
      {
        path: "digital-twin",
        element: (
          <Suspended>
            <DigitalTwinPage />
          </Suspended>
        ),
      },
      {
        path: "bom",
        element: (
          <Suspended>
            <BOMPage />
          </Suspended>
        ),
      },
      {
        path: "compliance",
        element: (
          <Suspended>
            <CompliancePage />
          </Suspended>
        ),
      },
      {
        path: "testing",
        element: (
          <Suspended>
            <TestingPage />
          </Suspended>
        ),
      },
      {
        path: "supply-chain",
        element: (
          <Suspended>
            <SupplyChainPage />
          </Suspended>
        ),
      },
      {
        path: "approvals",
        element: (
          <Suspended>
            <ApprovalsPage />
          </Suspended>
        ),
      },
      {
        path: "settings",
        element: (
          <Suspended>
            <SettingsPage />
          </Suspended>
        ),
      },
    ],
  },
];

// ---------------------------------------------------------------------------
// Router instance & provider
// ---------------------------------------------------------------------------

const router = createBrowserRouter(routes);

export function AppRouter() {
  return <RouterProvider router={router} />;
}

src/routes/layout.tsx

Application shell that provides the sidebar, topbar, breadcrumbs, and main content outlet. Responsive: sidebar collapses to an icon rail on narrow viewports and becomes a slide-out sheet on mobile.

import { Outlet, useLocation, Link } from "react-router";
import { useMediaQuery } from "@/hooks/use-media-query";
import { useUIStore } from "@/store/ui-store";
import { Sidebar } from "@/components/sidebar";
import { Topbar } from "@/components/topbar";
import { Sheet, SheetContent } from "@/components/ui/sheet";
import { cn } from "@/lib/utils";

// ---------------------------------------------------------------------------
// Breadcrumb helpers
// ---------------------------------------------------------------------------

interface Crumb {
  label: string;
  href: string;
}

const ROUTE_LABELS: Record<string, string> = {
  "": "Dashboard",
  sessions: "Sessions",
  agents: "Agents",
  "digital-twin": "Digital Twin",
  bom: "Bill of Materials",
  compliance: "Compliance",
  testing: "Testing",
  "supply-chain": "Supply Chain",
  approvals: "Approvals",
  settings: "Settings",
};

function useBreadcrumbs(): Crumb[] {
  const { pathname } = useLocation();
  const segments = pathname.split("/").filter(Boolean);

  const crumbs: Crumb[] = [{ label: "Dashboard", href: "/" }];

  let path = "";
  for (const segment of segments) {
    path += `/${segment}`;
    const label = ROUTE_LABELS[segment] ?? segment;
    crumbs.push({ label, href: path });
  }

  return crumbs;
}

// ---------------------------------------------------------------------------
// Breadcrumbs component
// ---------------------------------------------------------------------------

function Breadcrumbs() {
  const crumbs = useBreadcrumbs();

  return (
    <nav aria-label="Breadcrumb" className="flex items-center gap-1.5 text-sm text-muted-foreground px-6 py-2 border-b">
      {crumbs.map((crumb, index) => {
        const isLast = index === crumbs.length - 1;
        return (
          <span key={crumb.href} className="flex items-center gap-1.5">
            {index > 0 && (
              <span aria-hidden="true" className="text-muted-foreground/50">
                /
              </span>
            )}
            {isLast ? (
              <span className="font-medium text-foreground">{crumb.label}</span>
            ) : (
              <Link
                to={crumb.href}
                className="hover:text-foreground transition-colors"
              >
                {crumb.label}
              </Link>
            )}
          </span>
        );
      })}
    </nav>
  );
}

// ---------------------------------------------------------------------------
// Layout
// ---------------------------------------------------------------------------

export function Layout() {
  const sidebarOpen = useUIStore((s) => s.sidebarOpen);
  const sidebarCollapsed = useUIStore((s) => s.sidebarCollapsed);
  const toggleSidebar = useUIStore((s) => s.toggleSidebar);

  const isMobile = useMediaQuery("(max-width: 768px)");

  return (
    <div className="flex h-screen overflow-hidden bg-background text-foreground">
      {/* ---- Desktop sidebar ---- */}
      {!isMobile && (
        <aside
          className={cn(
            "hidden md:flex flex-col border-r bg-sidebar transition-[width] duration-200",
            sidebarCollapsed ? "w-16" : "w-64",
          )}
        >
          <Sidebar collapsed={sidebarCollapsed} />
        </aside>
      )}

      {/* ---- Mobile sidebar (sheet / drawer) ---- */}
      {isMobile && (
        <Sheet open={sidebarOpen} onOpenChange={toggleSidebar}>
          <SheetContent side="left" className="w-72 p-0">
            <Sidebar collapsed={false} />
          </SheetContent>
        </Sheet>
      )}

      {/* ---- Main column ---- */}
      <div className="flex flex-1 flex-col overflow-hidden">
        <Topbar />
        <Breadcrumbs />

        <main className="flex-1 overflow-y-auto p-6">
          <Outlet />
        </main>
      </div>
    </div>
  );
}

src/components/ui/page-skeleton.tsx

Full-page loading skeleton shown while lazy-loaded pages are being fetched.

import { Skeleton } from "@/components/ui/skeleton";

/**
 * Placeholder skeleton that mirrors a typical page layout.
 * Used as the Suspense fallback for lazy-loaded routes.
 */
export function PageSkeleton() {
  return (
    <div className="space-y-6 animate-in fade-in duration-300">
      {/* Page header */}
      <div className="space-y-2">
        <Skeleton className="h-8 w-48" />
        <Skeleton className="h-4 w-96" />
      </div>

      {/* Stat cards row */}
      <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
        {Array.from({ length: 4 }).map((_, i) => (
          <Skeleton key={i} className="h-28 rounded-xl" />
        ))}
      </div>

      {/* Main content area */}
      <Skeleton className="h-96 rounded-xl" />
    </div>
  );
}

src/hooks/use-media-query.ts

Utility hook used by the layout to detect viewport breakpoints.

import { useEffect, useState } from "react";

/**
 * Subscribe to a CSS media query and return whether it currently matches.
 *
 * @param query - A valid CSS media query string, e.g. "(max-width: 768px)".
 */
export function useMediaQuery(query: string): boolean {
  const [matches, setMatches] = useState(() => {
    if (typeof window === "undefined") return false;
    return window.matchMedia(query).matches;
  });

  useEffect(() => {
    const mql = window.matchMedia(query);

    function onChange(event: MediaQueryListEvent) {
      setMatches(event.matches);
    }

    mql.addEventListener("change", onChange);
    // Sync in case the value changed between render and effect.
    setMatches(mql.matches);

    return () => mql.removeEventListener("change", onChange);
  }, [query]);

  return matches;
}