Skip to main content
The template uses React Router DOM v7’s object-based routing API. All route definitions live in src/routes/, keeping them separate from page components.

How the router is set up

There are two files in src/routes/:

AppRoutes.tsx

Declares the full route tree as a RouteObject[] array. Import and add routes here.

index.tsx

Calls createBrowserRouter with the route array and exports the router instance used by main.tsx.

src/routes/index.tsx

src/routes/index.tsx
import { createBrowserRouter } from 'react-router-dom';
import AppRoutes from './AppRoutes';

export const router = createBrowserRouter(AppRoutes);
createBrowserRouter uses the HTML5 History API for clean URLs (no # hash). The exported router is passed to <RouterProvider> in src/App.tsx, which main.tsx then renders inside <StrictMode>.

The route tree

src/routes/AppRoutes.tsx
import type { RouteObject } from 'react-router-dom';
import { Layout } from '../components/Layout';
import { HomePage } from '../pages/HomePage';
import { FeaturesPage } from '../pages/FeaturesPage';
import { AboutPage } from '../pages/AboutPage';

const AppRoutes: RouteObject[] = [
    {
        path: '/',
        element: <Layout />,
        children: [
            { index: true, element: <HomePage /> },
            { path: 'features', element: <FeaturesPage /> },
            { path: 'about', element: <AboutPage /> },
        ],
    },
];

export default AppRoutes;

Built-in routes

PathComponentDescription
/HomePageLanding page (index route)
/featuresFeaturesPageFeature showcase grid
/aboutAboutPageAbout page

Nested routes and <Outlet />

Layout is the parent route at path: '/'. Every page is a child route rendered inside it. React Router replaces <Outlet /> in Layout with whichever child matches the current URL:
src/components/Layout.tsx
import { Outlet } from 'react-router-dom';
import { Navigation } from './Navigation';

export function Layout() {
    return (
        <div className="min-h-screen flex flex-col bg-gray-50">
            <Navigation />
            <main className="grow container mx-auto p-4 md:p-8">
                <Outlet />
            </main>
            <footer className="bg-gray-200 p-4 text-center text-gray-600 border-t">
                {new Date().getFullYear()} | Vite React Template by @RonConCoca
            </footer>
        </div>
    );
}
This means:
  • The navbar and footer render once, for every page.
  • You only need to write the content inside <main> in each page component.
  • Navigating between pages replaces only the <Outlet /> content — the shell stays mounted.

Adding a new route

1

Create the page component

Add a new file under src/pages/:
src/pages/ContactPage.tsx
export function ContactPage() {
    return (
        <div>
            <h1 className="text-3xl font-bold">Contact</h1>
            <p className="mt-2 text-gray-600">Get in touch with us.</p>
        </div>
    );
}
2

Register the route in AppRoutes.tsx

Import your page and add a new child route object:
src/routes/AppRoutes.tsx
import { ContactPage } from '../pages/ContactPage';

const AppRoutes: RouteObject[] = [
    {
        path: '/',
        element: <Layout />,
        children: [
            { index: true, element: <HomePage /> },
            { path: 'features', element: <FeaturesPage /> },
            { path: 'about', element: <AboutPage /> },
            { path: 'contact', element: <ContactPage /> }, // new
        ],
    },
];
3

Link to it from Navigation

Add a new entry to the links array in src/components/Navigation.tsx so users can reach the new page. See Navigation for the exact pattern.

Adding a 404 route

To catch unmatched URLs, add a catch-all route as the last child with path: '*':
import { NotFoundPage } from '../pages/NotFoundPage';

children: [
    { index: true, element: <HomePage /> },
    { path: 'features', element: <FeaturesPage /> },
    { path: 'about', element: <AboutPage /> },
    { path: '*', element: <NotFoundPage /> }, // catch-all
],
The catch-all child route still renders inside Layout, so your navbar and footer remain visible on the 404 page. If you want a fully custom error screen, add the errorElement property to the parent route instead.

Build docs developers (and LLMs) love