Routing
Learn how to use and customize the routing system in Metronic React.
Metronic React uses React Router v7 for handling navigation between different pages. This guide explains the routing system architecture and how to work with it.
Routing Architecture
The routing system is organized around these key files:
- app-routing.tsx: Main router configuration
- app-routing-setup.tsx: Route definitions
- require-auth.tsx: Route protection component
- auth-routing.tsx: Authentication routes
Main Router Setup
The main router is defined in src/routing/app-routing.tsx
:
// src/routing/app-routing.tsx
import { Suspense } from 'react';
import { BrowserRouter, useRoutes } from 'react-router-dom';
import { AppRoutingSetup } from './app-routing-setup';
import TopBarProgress from 'react-top-loading-bar';
import { useProgress } from '@/hooks/use-progress';
const Routing = () => {
const { progress } = useProgress();
const routes = useRoutes(AppRoutingSetup());
return (
<>
<TopBarProgress
color='#4338ca'
progress={progress}
/>
<Suspense fallback={<div>Loading...</div>}>
{routes}
</Suspense>
</>
);
};
export function AppRouting() {
return (
<BrowserRouter basename="/metronic/tailwind/react">
<Routing />
</BrowserRouter>
);
}
Note the basename
prop in BrowserRouter
, which sets the base URL for all routes. This matches the base
configuration in vite.config.ts
.
Route Definitions
Routes are defined in src/routing/app-routing-setup.tsx
:
// src/routing/app-routing-setup.tsx
import { ReactElement } from 'react';
import { Navigate, Route, Routes } from 'react-router';
import { DefaultPage } from '@/pages/dashboards';
import { RequireAuth } from '@/auth/require-auth';
import { Demo3Layout } from '@/layouts/demo3/layout';
import { ErrorRouting } from '@/errors/error-routing';
import { AuthRouting } from '@/auth/auth-routing';
const AppRoutingSetup = (): ReactElement => {
return (
<Routes>
<Route element={<RequireAuth />}>
<Route element={<Demo3Layout />}>
<Route path="/" element={<DefaultPage />} />
{/* Other protected routes... */}
</Route>
</Route>
<Route path="error/*" element={<ErrorRouting />} />
<Route path="auth/*" element={<AuthRouting />} />
<Route path="*" element={<Navigate to="/error/404" />} />
</Routes>
);
};
export { AppRoutingSetup };
Protected Routes
Routes that require authentication are wrapped with the RequireAuth
component:
// src/auth/require-auth.tsx
import { useEffect } from 'react';
import { useNavigate, Outlet } from 'react-router-dom';
import { useAuth } from './use-auth';
export function RequireAuth() {
const { currentUser, isLoading } = useAuth();
const navigate = useNavigate();
useEffect(() => {
if (!isLoading && !currentUser) {
navigate('/auth/login');
}
}, [currentUser, isLoading, navigate]);
return currentUser ? <Outlet /> : null;
}
Authentication Routes
Authentication routes are defined in src/auth/auth-routing.tsx
:
// src/auth/auth-routing.tsx
import { lazy } from 'react';
import { Routes, Route, Navigate } from 'react-router-dom';
import { AuthLayout } from '@/layouts/auth/layout';
const LoginPage = lazy(() => import('@/pages/auth/login-page'));
const RegisterPage = lazy(() => import('@/pages/auth/register-page'));
const ForgotPasswordPage = lazy(() => import('@/pages/auth/forgot-password-page'));
export function AuthRouting() {
return (
<Routes>
<Route element={<AuthLayout />}>
<Route path="login" element={<LoginPage />} />
<Route path="register" element={<RegisterPage />} />
<Route path="forgot-password" element={<ForgotPasswordPage />} />
<Route path="*" element={<Navigate to="/auth/login" />} />
</Route>
</Routes>
);
}
Using Layouts
Layouts are used to provide a consistent UI structure for multiple routes. In Metronic React, layouts are implemented as route wrappers:
// src/layouts/demo3/layout.tsx
import { Outlet } from 'react-router-dom';
import { Header } from './components/header';
import { Sidebar } from './components/sidebar';
import { Footer } from './components/footer';
export function Demo3Layout() {
return (
<div className="layout">
<Header />
<div className="flex">
<Sidebar />
<main className="content">
<Outlet />
</main>
</div>
<Footer />
</div>
);
}
The Outlet
component is where the matched route content will be rendered.
Navigation
Link Component
For navigation between routes, use the Link
component from React Router:
import { Link } from 'react-router-dom';
// Basic link
<Link to="/dashboard">Dashboard</Link>
// Link with state
<Link
to="/profile"
state={{ from: 'dashboard' }}
>
Profile
</Link>
Programmatic Navigation
For programmatic navigation (e.g., after form submission), use the useNavigate
hook:
import { useNavigate } from 'react-router-dom';
function LoginForm() {
const navigate = useNavigate();
const handleLogin = async (formData) => {
// Login logic...
if (success) {
navigate('/dashboard');
}
};
// ...
}
Route Parameters
You can define dynamic routes with parameters:
// In app-routing-setup.tsx
<Route path="/profile/:id" element={<ProfilePage />} />
// In ProfilePage component
import { useParams } from 'react-router-dom';
function ProfilePage() {
const { id } = useParams();
// Use the id parameter
return <div>Profile ID: {id}</div>;
}
Nested Routes
Nested routes allow you to organize related routes:
// In app-routing-setup.tsx
<Route path="/settings" element={<SettingsLayout />}>
<Route index element={<GeneralSettings />} />
<Route path="profile" element={<ProfileSettings />} />
<Route path="security" element={<SecuritySettings />} />
</Route>
// In SettingsLayout component
import { Outlet } from 'react-router-dom';
function SettingsLayout() {
return (
<div className="settings-layout">
<SettingsSidebar />
<div className="settings-content">
<Outlet />
</div>
</div>
);
}
Route Meta Data
To associate meta data with routes, you can create a route configuration object:
// src/routing/routes.ts
export const routes = [
{
path: '/',
element: <DefaultPage />,
meta: {
title: 'Dashboard',
breadcrumb: 'Home'
}
},
{
path: '/profile',
element: <ProfilePage />,
meta: {
title: 'User Profile',
breadcrumb: 'Profile'
}
}
];
Then use this configuration to generate routes and access meta data:
// Example usage in a component
import { useLocation } from 'react-router-dom';
import { routes } from '@/routing/routes';
function PageTitle() {
const location = useLocation();
const route = routes.find(r => r.path === location.pathname);
const title = route?.meta?.title || 'Metronic';
return <h1>{title}</h1>;
}
Error Routes
Error routes are defined in src/errors/error-routing.tsx
and handle various error states:
// src/errors/error-routing.tsx
import { lazy } from 'react';
import { Routes, Route, Navigate } from 'react-router-dom';
import { ErrorLayout } from '@/layouts/error/layout';
const Error404Page = lazy(() => import('@/pages/errors/error-404-page'));
const Error500Page = lazy(() => import('@/pages/errors/error-500-page'));
export function ErrorRouting() {
return (
<Routes>
<Route element={<ErrorLayout />}>
<Route path="404" element={<Error404Page />} />
<Route path="500" element={<Error500Page />} />
<Route path="*" element={<Navigate to="/error/404" />} />
</Route>
</Routes>
);
}
Loading Indicator
Metronic React includes a top loading bar to show route navigation progress:
// Usage in app-routing.tsx
import TopBarProgress from 'react-top-loading-bar';
import { useProgress } from '@/hooks/use-progress';
const Routing = () => {
const { progress } = useProgress();
return (
<>
<TopBarProgress
color='#4338ca'
progress={progress}
/>
{/* Routes */}
</>
);
};
For more details, see the source code in the src/routing
directory.