Custom Routes
Add pages beyond the resource CRUD routes.
Not every page is a resource view. <CustomRoutes> lets you add arbitrary routes
to the admin — a settings screen, a dashboard, a public marketing page — that
render either inside the layout or standalone.
Adding routes
Drop <CustomRoutes> among your <Resource>s and give it <Route> children.
Under the default react-router adapter these are react-router <Route>s:
import { Admin, CustomRoutes, Layout, Resource } from "@chelone/react";
import { Route } from "react-router";
import { AboutPage } from "./about";
import { dataProvider, queryClient } from "./providers";
import { PostList } from "./posts";
import { SettingsPage } from "./settings";
export function App() {
return (
<Admin dataProvider={dataProvider} queryClient={queryClient}>
<Admin.Layout>
<Layout />
</Admin.Layout>
<Resource name="posts" list={PostList} />
{/* inside the layout — has the sidebar, app bar, and auth gate */}
<CustomRoutes>
<Route path="/settings" element={<SettingsPage />} />
</CustomRoutes>
{/* standalone — no layout, auth-agnostic */}
<CustomRoutes noLayout>
<Route path="/about" element={<AboutPage />} />
</CustomRoutes>
</Admin>
);
}Under another router adapter, use that adapter's own Route instead (see
Admin).
Layout vs standalone
- Default (
<CustomRoutes>): the route renders inside<Layout>— it gets the sidebar, app bar, and (whenrequireAuth) the auth gate. Use it for admin pages that belong in the shell. noLayout: the route renders standalone, outside the layout and auth-agnostic. Use it for login-adjacent or public pages that shouldn't show the admin chrome.
You can mix both — one <CustomRoutes> for in-shell pages, another with
noLayout for public ones.
Linking to a custom route
Use the router adapter's navigation, useRedirect or useNavigate, so links
work under whichever router adapter the admin runs on. A function passed to
useRedirect returns the raw path, which gets the app basename prepended:
import { useRedirect } from "@chelone/react";
export function SettingsButton() {
const redirect = useRedirect();
return (
<button type="button" onClick={() => redirect(() => "/settings")}>
Settings
</button>
);
}useNavigate takes the path directly: navigate("/settings").
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | The `<Route>` elements (or a fragment of them). |
| noLayout | boolean | — | When true these routes render STANDALONE (outside `<Layout>`, auth-agnostic — parity §0a-10): login-adjacent pages, public pages. When false/omitted they render inside `<Layout>` (can override resource paths by order). |
Tips
noLayoutroutes render outside the auth gate — put anything that must stay public (a login page, a marketing page) here.- With-layout custom routes are matched before resource routes, wherever the
<CustomRoutes>sits among the children, so a custom route overrides a resource path it matches exactly. What a broader pattern does depends on the adapter. react-router ranks by specificity first, so/:slugdoes not take over/posts. The TanStack adapter takes the first match in order, so there/:slugdoes take over/posts.