Needs major update! Don't follow it!
React Router v7 introduced the Data Router API, a declarative and more powerful way to define routes, load data, handle actions, and manage layouts. Unlike the traditional approach using BrowserRouter, Routes, and Route, the data router approach uses functions like createBrowserRouter and RouterProvider to define routing, layouts, nested routes, and more.
Use this guide if:
- You want a clean, nested layout structure
- You plan to use loaders, actions, or error boundaries
- You are using Vite/React and want a modern routing setup
π Install React Router
npm i react-router
ποΈ Create routes/index.tsx
You can use either Component or element, but with the Data Router API, prefer Component.
import App from "@/App";
import Tasks from "@/pages/Tasks";
import User from "@/pages/User";
import UserDetails from "@/pages/UserDetails";
import NotFound from "@/pages/NotFound";
import { createBrowserRouter } from "react-router-dom";
const router = createBrowserRouter([
{
path: "/",
Component: App,
children: [
{
index: true,
element: <Tasks />, // Show on homepage
},
{
path: "tasks",
element: <Tasks />,
},
{
path: "users",
element: <User />,
},
{
path: "users/:id",
element: <UserDetails />,
},
{
path: "*",
element: <NotFound />, // 404 Page
},
],
},
]);
export default router;
π§΅ Setup main.tsx
Make sure to use RouterProvider with the created router. If you're using state management (like Redux) or a UI library (like shadcn), wrap accordingly.
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import { Provider } from "react-redux";
import { store } from "./redux/store";
import { RouterProvider } from "react-router-dom";
import router from "./routes/index";
import { ThemeProvider } from "./providers/theme-provider";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<Provider store={store}>
<RouterProvider router={router} />
</Provider>
</ThemeProvider>
</StrictMode>
);
π§© Define Layout with <Outlet /> in App.tsx
Child routes render wherever <Outlet /> is used. Without it, nothing will render.
import { Outlet } from "react-router-dom";
import Navbar from "@/components/layout/Navbar";
function App() {
return (
<>
<Navbar />
<Outlet />
</>
);
}
export default App;
π§ͺ Example Page Component - Tasks
const Tasks = () => {
return (
<div>
<h1>This is Tasks component</h1>
</div>
);
};
export default Tasks;
π Dynamic Route Example - users/:id
import { useParams } from "react-router-dom";
const UserDetails = () => {
const { id } = useParams();
return <div>User ID: {id}</div>;
};
export default UserDetails;
β 404 Page Example
const NotFound = () => {
return <h1>404 - Page Not Found</h1>;
};
export default NotFound;
π Key Notes
- β
Use
index: trueto render default content on/. - β
Include
<Outlet />in your layout to render child routes. - β
Use
path: "*"for fallback 404 pages. - β
Use
useParams()to extract dynamic values like:id. - β You can share state/context (Redux, Theme) using top-level providers.
π¦ What Youβve Built So Far
- β
Modern nested routing with layout (
App.tsx) - β Default (index) route
- β 404 not found handler
- β
Dynamic route (
users/:id)
β What's Next (Optional Add-ons)
1. π€ Loader and Action Route Example
import { createBrowserRouter } from "react-router-dom";
import Tasks from "@/pages/Tasks";
const router = createBrowserRouter([
{
path: "/tasks",
element: <Tasks />, // fallback to "element" usage here for loader/action
loader: async () => {
const res = await fetch("/api/tasks");
return res.json();
},
action: async ({ request }) => {
const formData = await request.formData();
return fetch("/api/tasks", {
method: "POST",
body: formData,
});
},
},
]);
2. π Protected Routes Full Example
import { createBrowserRouter } from "react-router-dom";
import ProtectedLayout from "@/layouts/ProtectedLayout";
import Profile from "@/pages/Profile";
import Login from "@/pages/Login";
const router = createBrowserRouter([
{
path: "/login",
element: <Login />,
},
{
path: "/dashboard",
Component: ProtectedLayout,
children: [
{
path: "profile",
element: <Profile />,
},
],
},
]);
3. π§± Separate Layouts Example
import { createBrowserRouter } from "react-router-dom";
import MainLayout from "@/layouts/MainLayout";
import AdminLayout from "@/layouts/AdminLayout";
import Home from "@/pages/Home";
import About from "@/pages/About";
import AdminDashboard from "@/pages/AdminDashboard";
const router = createBrowserRouter([
{
path: "/",
Component: MainLayout,
children: [
{ index: true, element: <Home /> },
{ path: "about", element: <About /> },
],
},
{
path: "/admin",
Component: AdminLayout,
children: [{ path: "dashboard", element: <AdminDashboard /> }],
},
]);
4. π§ Nested Routes Example
import { createBrowserRouter } from "react-router-dom";
import UserLayout from "@/layouts/UserLayout";
import UserList from "@/pages/UserList";
import UserDetails from "@/pages/UserDetails";
const router = createBrowserRouter([
{
path: "/users",
Component: UserLayout,
children: [
{ index: true, element: <UserList /> },
{ path: ":id", element: <UserDetails /> },
],
},
]);
Top comments (0)