Web Development

React Router Tutorial: The Complete Routing Guide for 2026

B

Boundev Team

Feb 19, 2026
8 min read
React Router Tutorial: The Complete Routing Guide for 2026

Master React Router with this hands-on guide. Learn nested routes, data loaders, actions, and protected routes to build professional single-page applications.

What You'll Learn

How to set up React Router from scratch with createBrowserRouter.
Building nested routes with shared layouts using the Outlet component.
Fetching data before a page renders using loaders.
Handling form submissions and mutations with actions.
Implementing protected routes for authenticated users.

Routing is the backbone of every React application. Without it, your app is a single page with no way to navigate. At Boundev, React Router is a non-negotiable part of our frontend stack. This guide covers everything you need to know—from basic setup to advanced patterns like data loaders and actions.

Why React Router?

React itself has no built-in routing. Libraries like React Router fill that gap by mapping URL paths to components, enabling the seamless, instant navigation users expect from modern web applications. As of 2026, React Router v7 is the gold standard, offering a "data router" architecture that couples routing with data fetching and mutations.

Core Concepts at a Glance

Routes

Map a URL path to a component

/dashboard → <Dashboard />

Loaders

Fetch data before rendering

No more loading spinners on mount

Actions

Handle mutations declaratively

Form submissions without boilerplate

1. Setting Up React Router

The modern approach uses createBrowserRouter and RouterProvider. This "data router" setup unlocks loaders, actions, and error boundaries.

import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import Root from './routes/Root';
import Dashboard from './routes/Dashboard';
import ErrorPage from './routes/ErrorPage';

const router = createBrowserRouter([
  {
    path: '/',
    element: <Root />,
    errorElement: <ErrorPage />,
    children: [
      { path: 'dashboard', element: <Dashboard /> },
    ],
  },
]);

function App() {
  return <RouterProvider router={router} />;
}

2. Nested Routes and Layouts

Nested routes are perhaps React Router's most powerful feature. They allow you to define a layout once (e.g., a sidebar and header) and have child routes render inside it via the <Outlet /> component.

// Root.jsx — The shared layout
import { Outlet, Link } from 'react-router-dom';

export default function Root() {
  return (
    <div className="app-layout">
      <nav>
        <Link to="/dashboard">Dashboard</Link>
        <Link to="/settings">Settings</Link>
      </nav>
      <main>
        <Outlet /> {/* Child routes render here */}
      </main>
    </div>
  );
}

Pro Tip

Think of nested routes as a "Russian doll" pattern. Each level adds UI around its children. The header stays mounted while child pages swap in and out—giving the feel of a native app.

3. Data Loading with Loaders

Before React Router's data APIs, developers had to fetch data inside useEffect, which caused the dreaded pattern of: render → show spinner → fetch → re-render. Loaders fix this by fetching data before the component mounts.

// Route definition with a loader
{
  path: 'dashboard',
  element: <Dashboard />,
  loader: async () => {
    const res = await fetch('/api/stats');
    return res.json();
  },
}

// Dashboard.jsx — Consuming loader data
import { useLoaderData } from 'react-router-dom';

export default function Dashboard() {
  const stats = useLoaderData();
  return <h1>Revenue: ${stats.revenue}</h1>;
}

4. Mutations with Actions

Actions handle data writes (POST, PUT, DELETE). When a user submits a <Form>, React Router calls the associated action, then automatically revalidates all loaders to keep the UI fresh.

import { Form, redirect } from 'react-router-dom';

// Route definition
{
  path: 'contacts/new',
  element: <NewContact />,
  action: async ({ request }) => {
    const formData = await request.formData();
    await createContact(Object.fromEntries(formData));
    return redirect('/contacts');
  },
}

// NewContact.jsx
export default function NewContact() {
  return (
    <Form method="post">
      <input name="name" placeholder="Name" />
      <button type="submit">Save</button>
    </Form>
  );
}

5. Dynamic Routes and URL Parameters

Dynamic segments (e.g., /users/:userId) let you build detail pages. The useParams hook extracts the value from the URL.

// Route
{ path: 'users/:userId', element: <UserProfile /> }

// UserProfile.jsx
import { useParams } from 'react-router-dom';

export default function UserProfile() {
  const { userId } = useParams();
  // Fetch user data using userId...
}

6. Protected Routes

For authenticated sections, use a loader to check the user's session. If they're not logged in, redirect them to the login page. This approach is more secure than client-side guards because it runs before any component renders.

{
  path: 'admin',
  element: <AdminPanel />,
  loader: async () => {
    const user = await getUser();
    if (!user) throw redirect('/login');
    return user;
  },
}

7. Error Handling

React Router lets you define an errorElement per route. If a loader throws or a component crashes, the error element renders instead of a blank screen.

{
  path: 'dashboard',
  element: <Dashboard />,
  errorElement: <ErrorPage />, // Displays if loader or component throws
  loader: dashboardLoader,
}

Essential Hooks Cheat Sheet

Hook Purpose Example
useNavigate Programmatic navigation navigate('/home')
useParams Read URL params const { id } = useParams()
useLoaderData Access loader results const data = useLoaderData()
useLocation Read current URL info const loc = useLocation()
useSubmit Trigger actions imperatively submit(formData, { method: 'post' })

Build Scalable React Applications

From routing architecture to production deployment, Boundev's engineering teams build React applications that scale effortlessly.

Talk to Our React Experts

Frequently Asked Questions

Do I need React Router for every React project?

Not necessarily. If your app is a single-screen widget or a simple form, you don't need routing. However, any application with more than one "page" or view benefits greatly from React Router.

What is the difference between BrowserRouter and createBrowserRouter?

BrowserRouter is the older, component-based approach. createBrowserRouter is the newer "data router" API that unlocks loaders, actions, and errorElement. For new projects, always use createBrowserRouter.

How do I handle 404 pages with React Router?

Add a catch-all route at the end of your route configuration with path: '*'. This matches any URL that doesn't correspond to a defined route, and you can render a custom "Page Not Found" component.

Can I use React Router with Next.js?

No. Next.js has its own file-based routing system. React Router is designed for client-side SPAs or Vite-based projects. Mixing the two would create conflicts.

How do loaders improve performance over useEffect?

With useEffect, the component renders first (often showing a spinner), then fetches data, then re-renders. Loaders fetch data in parallel with the route transition, so the component renders with data already available—eliminating layout shift and perceived lag.

Tags

#React#React Router#JavaScript#Frontend#Single Page Application
B

Boundev Team

At Boundev, we're passionate about technology and innovation. Our team of experts shares insights on the latest trends in AI, software development, and digital transformation.

Ready to Transform Your Business?

Let Boundev help you leverage cutting-edge technology to drive growth and innovation.

Get in Touch

Start Your Journey Today

Share your requirements and we'll connect you with the perfect developer within 48 hours.

Get in Touch