Routing
SmartMaker uses React Router to manage navigation between application pages (Single Page Application).
Basic Configuration
Create the Router
// src/components/app/Router/index.jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { LoginPage } from '../../pages/public/LoginPage';
import { HomePage } from '../../pages/private/HomePage';
import { Error404Page } from '../../pages/errors/Error404Page';
export const Router = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/" element={<HomePage />} />
<Route path="*" element={<Error404Page />} />
</Routes>
</BrowserRouter>
);
};
Integrate into App.jsx
// src/App.jsx
import { Provider } from '@cap-rel/smartcommon';
import { Router } from './components/app/Router';
import { config } from './appConfig';
export const App = () => {
return (
<Provider config={config}>
<Router />
</Provider>
);
};
Protected Routes
Tip
With smartcommon, use <RouteGuard>: it covers 4 modes (requireAuth, requireGuest, requireDeviceIdentification, requireDeviceIdentified) and directly reads useApi().user / user.deviceOptions. See Advanced Components -> RouteGuard for the full version. The manual version below remains useful pedagogically or for business rules that do not fit these 4 modes.
RouteGuard (Recommended)
import { RouteGuard } from '@cap-rel/smartcommon';
<Routes>
<Route element={<RouteGuard requireGuest />}>
<Route path="/login" element={<LoginPage />} />
<Route path="/welcome" element={<WelcomePage />} />
</Route>
<Route element={<RouteGuard requireDeviceIdentified />}>
<Route path="/" element={<HomePage />} />
<Route path="/settings" element={<SettingsPage />} />
</Route>
</Routes>
Authentication Guards (Manual)
// src/components/app/Router/Guards/index.jsx
import { Outlet, Navigate } from 'react-router-dom';
import { useGlobalStates } from '@cap-rel/smartcommon';
/**
* Public routes (login, register, etc.)
* Redirects to / if already logged in
*/
export const PublicRoutes = () => {
const gst = useGlobalStates();
const session = gst.get('session');
if (session) {
return <Navigate to="/" replace />;
}
return <Outlet />;
};
/**
* Private routes (home, settings, etc.)
* Redirects to /login if not authenticated
*/
export const PrivateRoutes = () => {
const gst = useGlobalStates();
const session = gst.get('session');
if (!session) {
return <Navigate to="/login" replace />;
}
return <Outlet />;
};
Using Guards
// src/components/app/Router/index.jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { PublicRoutes, PrivateRoutes } from './Guards';
// Public pages
import { WelcomePage } from '../../pages/public/WelcomePage';
import { LoginPage } from '../../pages/public/LoginPage';
// Private pages
import { HomePage } from '../../pages/private/HomePage';
import { SettingsPage } from '../../pages/private/SettingsPage';
import { ItemPage } from '../../pages/private/ItemPage';
// Errors
import { Error404Page } from '../../pages/errors/Error404Page';
export const Router = () => {
return (
<BrowserRouter>
<Routes>
{/* Public routes */}
<Route element={<PublicRoutes />}>
<Route path="/welcome" element={<WelcomePage />} />
<Route path="/login" element={<LoginPage />} />
</Route>
{/* Private routes */}
<Route element={<PrivateRoutes />}>
<Route path="/" element={<HomePage />} />
<Route path="/settings" element={<SettingsPage />} />
<Route path="/items/:id" element={<ItemPage />} />
</Route>
{/* Fallback 404 */}
<Route path="*" element={<Error404Page />} />
</Routes>
</BrowserRouter>
);
};
Navigation
useNavigation Hook
SmartCommon exposes useNavigation which returns an object with navigation methods:
import { useNavigation } from '@cap-rel/smartcommon';
const MyComponent = () => {
const nav = useNavigation();
return (
<div>
{/* Simple navigation */}
<button onClick={() => nav.navigate('/')}>Home</button>
<button onClick={() => nav.navigate('/settings')}>Settings</button>
{/* With parameters */}
<button onClick={() => nav.navigate(`/items/${itemId}`)}>View item</button>
{/* Go back */}
<button onClick={() => nav.navigate(-1)}>Back</button>
{/* Replace history (chainable builder) */}
<button onClick={() => nav.replace().to('/login')}>
Logout
</button>
</div>
);
};
Returned Properties
| Property | Description |
|---|---|
nav.navigate(to, options) |
React Router navigation function |
nav.params |
Route parameters (useParams) |
nav.searchParams |
Query string (useSearchParams) |
nav.location |
Current location object |
nav.history |
Navigation history |
nav.replace() |
Chainable builder: replace instead of push |
nav.state(value) |
Chainable builder: pass a state |
nav.to(path) |
Chainable builder: execute navigation |
Link Component
For simple links, use the Link component:
import { Link } from 'react-router-dom';
const Navigation = () => {
return (
<nav>
<Link to="/">Home</Link>
<Link to="/settings">Settings</Link>
<Link to="/items/123">Item 123</Link>
</nav>
);
};
Route Parameters
Dynamic Parameters
// Route with parameter :id
<Route path="/items/:id" element={<ItemPage />} />
// Get the parameter
import { useParams } from 'react-router-dom';
const ItemPage = () => {
const { id } = useParams(); // id = "123" for /items/123
return <div>Item #{id}</div>;
};
Multiple Parameters
// Route with multiple parameters
<Route path="/users/:userId/posts/:postId" element={<PostPage />} />
const PostPage = () => {
const { userId, postId } = useParams();
// ...
};
Query Strings
import { useSearchParams } from 'react-router-dom';
const SearchPage = () => {
const [searchParams, setSearchParams] = useSearchParams();
// Read: /search?q=test&page=2
const query = searchParams.get('q'); // "test"
const page = searchParams.get('page'); // "2"
// Modify
const handleSearch = (newQuery) => {
setSearchParams({ q: newQuery, page: '1' });
};
return (
<input
value={query || ''}
onChange={(e) => handleSearch(e.target.value)}
/>
);
};
Nested Routes
Shared Layout
// src/components/app/Router/index.jsx
<Routes>
<Route element={<PrivateRoutes />}>
{/* Layout with bottom navigation */}
<Route element={<MainLayout />}>
<Route path="/" element={<HomePage />} />
<Route path="/search" element={<SearchPage />} />
<Route path="/profile" element={<ProfilePage />} />
</Route>
{/* Pages without bottom navigation */}
<Route path="/items/:id" element={<ItemPage />} />
<Route path="/settings" element={<SettingsPage />} />
</Route>
</Routes>
Layout Component
// src/components/layouts/MainLayout/index.jsx
import { Outlet } from 'react-router-dom';
import { useNavigation } from '@cap-rel/smartcommon';
export const MainLayout = () => {
const nav = useNavigation();
return (
<div className="min-h-screen flex flex-col">
{/* Page content (Outlet = route child) */}
<main className="flex-1">
<Outlet />
</main>
{/* Fixed bottom navigation */}
<nav className="bg-white shadow-lg p-2 flex justify-around">
<button onClick={() => nav.navigate('/')}>Home</button>
<button onClick={() => nav.navigate('/search')}>Search</button>
<button onClick={() => nav.navigate('/profile')}>Profile</button>
</nav>
</div>
);
};
Transition Animations
Configuration in appConfig
// appConfig.js
export const config = {
pages: {
// From home
"/": {
"/settings": "slideLeft", // Home -> Settings: slide left
"/items/*": "slideLeft", // Home -> Item: slide left
"*": "fade", // Others: fade
},
// From settings
"/settings": {
"/": "slideRight", // Settings -> Home: slide right
},
// Default
"*": "fade",
},
};
Available Animations
| Animation | Description |
|---|---|
fade |
Crossfade |
slideLeft |
Slide left |
slideRight |
Slide right |
zoom |
Zoom in/out |
See Animations for more details.
Error Handling
404 Page
// src/components/pages/errors/Error404Page/index.jsx
import { useNavigation } from '@cap-rel/smartcommon';
export const Error404Page = () => {
const nav = useNavigation();
return (
<div className="min-h-screen flex flex-col items-center justify-center p-4">
<h1 className="text-6xl font-bold text-gray-300 mb-4">404</h1>
<p className="text-gray-600 mb-8">Page not found</p>
<button
onClick={() => nav.navigate('/')}
className="bg-primary text-white px-6 py-3 rounded-lg"
>
Back to Home
</button>
</div>
);
};
Conditional Redirect
import { Navigate, useLocation } from 'react-router-dom';
import { useGlobalStates } from '@cap-rel/smartcommon';
const RequireAuth = ({ children }) => {
const gst = useGlobalStates();
const session = gst.get('session');
const location = useLocation();
if (!session) {
// Save URL to redirect after login
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children;
};
Best Practices
Route Structure
- Group routes by type (public, private, errors)
- Use guards for protection
- Place the
*fallback last
Navigation
- Prefer SmartCommon's
useNavigation - Use
replace: truefor redirects (login, logout) - Avoid hardcoded paths, use constants
Performance
- Lazy loading for large pages
- Light animations on mobile
- Preload critical data
See Also
- Animations - Page Transitions
- Components and Pages - Page Structure
- Hooks - useNavigation
- Configuration - Provider Options