DEV Community

Cover image for πŸ“ React Router Data APIs β€” The Complete Beginner-Friendly Guide (2025 Edition)
Vishwark
Vishwark

Posted on

πŸ“ React Router Data APIs β€” The Complete Beginner-Friendly Guide (2025 Edition)

A practical, real-world guide to loaders, actions, fetchers, nested routes, revalidation & more.


Modern React applications need more than just routingβ€”they need data loading, mutations, caching, redirects, pagination, filters, streaming, and error handling.

React Router v6.4+ introduced the Data APIs, turning React Router into a powerful client-side framework similar to Remix or Next.js (but much simpler).

This guide walks you through everything step by step, from setup to advanced patterns.


⭐ Table of Contents

  1. Why Data APIs?
  2. Project Setup (Vite + RouterProvider)
  3. Folder Structure
  4. Creating the Router
  5. Route Objects
  6. Loaders
  7. Actions
  8. Deferred Data (defer)
  9. Error Boundaries
  10. Navigation APIs
  11. Fetchers
  12. Data Caching & Revalidation
  13. Nested Routes & Layout Patterns
  14. Redirects & Search Params
  15. Final Thoughts

🎯 1. Why React Router Data APIs?

Before v6.4, React Router only handled navigation.
You had to manually manage:

  • useEffect data fetching
  • useState or Redux
  • Loading states
  • Error handling
  • Form submissions
  • Navigation states

This meant boilerplate everywhere.

Data APIs solve this:

  • Loaders β†’ fetch data before rendering
  • Actions β†’ run POST/PUT/DELETE mutations
  • Fetchers β†’ inline form actions without navigation
  • Navigation state β†’ built-in loading UI
  • Error boundaries β†’ per route
  • Streaming (defer) β†’ faster UIs
  • Route-based caching β†’ fewer network calls

React Router becomes both a router and a data layer.


πŸ› οΈ 2. Setup: Installing React Router (Vite + RouterProvider)

Create a Vite + React project:

npm create vite@latest my-app --template react
cd my-app
npm install react-router-dom
Enter fullscreen mode Exit fullscreen mode

Now add your router:


πŸ“ 3. Recommended Folder Structure

src/
  main.jsx
  router.jsx

  layouts/
    RootLayout.jsx

  pages/
    Home.jsx
    Dashboard.jsx

  errors/
    RootError.jsx

  services/
    api.js

  store/
    useAppStore.js
Enter fullscreen mode Exit fullscreen mode

This scales well for real-world applications.


πŸ”Œ 4. Creating The Router (router.jsx)

Here’s a minimal production-ready router using route objects:

import { createBrowserRouter } from "react-router-dom";

import RootLayout from "./layouts/RootLayout";
import RootError from "./errors/RootError";
import Home, { loader as homeLoader } from "./pages/Home";

export const router = createBrowserRouter([
  {
    path: "/",
    element: <RootLayout />,
    errorElement: <RootError />,
    children: [
      {
        index: true,
        element: <Home />,
        loader: homeLoader,
      },
      {
        path: "dashboard",
        lazy: async () => {
          const module = await import("./pages/Dashboard.jsx");
          return {
            Component: module.default,
            loader: module.loader,
          };
        }
      }
    ]
  }
]);
Enter fullscreen mode Exit fullscreen mode

πŸš€ 5. Mounting The Router (RouterProvider)

React Router Data APIs use RouterProvider, not BrowserRouter.

Update main.jsx:

import React from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider } from "react-router-dom";

import { router } from "./router";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
);
Enter fullscreen mode Exit fullscreen mode

βœ” No nested <Routes> needed
βœ” Router loads data automatically
βœ” Handles errors, loaders, lazy routes


🧱 6. Layout Setup (Root Layout)

Your root layout should include:

  • header/navigation
  • global loader logic
  • <Outlet /> for child routes
import { Outlet, NavLink, useNavigation } from "react-router-dom";

export default function RootLayout() {
  const navigation = useNavigation();
  const isLoading = navigation.state === "loading";

  return (
    <div>
      <nav>
        <NavLink to="/">Home</NavLink>
        <NavLink to="/dashboard">Dashboard</NavLink>
      </nav>

      {isLoading && <div className="loader">Loading...</div>}

      <Outlet />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

πŸ’₯ 7. Route Objects β€” The Core of Data API

Route objects allow:

  • loaders
  • actions
  • lazy loaded components
  • nested routes
  • per-route error boundaries

Example:

{
  path: "users",
  element: <UsersPage />,
  loader: usersLoader,
  action: usersAction,
  errorElement: <UsersError />
}
Enter fullscreen mode Exit fullscreen mode

πŸ“¦ 8. Loaders β€” Fetch Data Before Rendering

Loaders run before a route’s component mounts.

export async function loader() {
  const res = await fetch("/api/messages");
  return res.json();
}
Enter fullscreen mode Exit fullscreen mode

Use inside component:

const data = useLoaderData();
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • No flicker
  • No useEffect needed
  • Built-in caching
  • Auto revalidation after actions

πŸ“ 9. Actions β€” Handle Form Submissions

Actions run on:

  • <Form method="post">
  • fetcher.submit()
export async function action({ request }) {
  const form = await request.formData();
  const name = form.get("name");

  await fetch("/api/users", {
    method: "POST",
    body: JSON.stringify({ name })
  });

  return redirect("/users");
}
Enter fullscreen mode Exit fullscreen mode

⚑ 10. Deferred Data (defer) β€” Faster UIs

Perfect for dashboards, heavy reports, analytics pages.

import { defer } from "react-router-dom";

export function loader() {
  return defer({
    fast: fetch("/api/summary").then(r => r.json()),
    heavy: fetch("/api/big").then(r => r.json())
  });
}
Enter fullscreen mode Exit fullscreen mode

Use with <Await>:

<Suspense fallback="Loading...">
  <Await resolve={data.fast}>{renderFast}</Await>
</Suspense>

<Suspense fallback="Loading big...">
  <Await resolve={data.heavy}>{renderHeavy}</Await>
</Suspense>
Enter fullscreen mode Exit fullscreen mode

❗ 11. Error Boundaries β€” Per Route

Each route gets its own error UI:

{
  path: "users",
  errorElement: <UsersError />
}
Enter fullscreen mode Exit fullscreen mode

Use:

const err = useRouteError();
Enter fullscreen mode Exit fullscreen mode

Prevents global crashes.


🚦 12. Navigation APIs (useNavigation)

const navigation = useNavigation();
navigation.state; // "idle" | "loading" | "submitting"
Enter fullscreen mode Exit fullscreen mode

Usecases:

  • disable buttons
  • show skeletons
  • parent layout loaders

πŸš€ 13. Fetchers β€” Inline Mutations Without Navigation

Perfect for:

  • like buttons
  • toggles
  • delete actions
  • row-level edits
const fetcher = useFetcher();

<fetcher.Form method="post" action="/users/123/delete">
  <button>
    {fetcher.state === "submitting" ? "Deleting..." : "Delete"}
  </button>
</fetcher.Form>
Enter fullscreen mode Exit fullscreen mode

Child page stays mounted β†’ only data updates.


πŸ”„ 14. Data Caching & Revalidation

Loaders automatically re-run when:

  • Navigation to same page
  • Search params change
  • Actions complete
  • revalidate() called
  • Same route path, different params

Loaders DO NOT re-run for parent routes unless needed.


🧱 15. Nested Routes & Layout Patterns

Organize UI like real apps:

RootLayout
  └── DashboardLayout
        └── ReportsLayout
              β”œβ”€β”€ List
              └── Details
Enter fullscreen mode Exit fullscreen mode

Parent stays mounted β†’ child shows loader.
This gives fast, smooth UX.


πŸ” 16. Redirects & Search Params

redirect()

throw redirect("/login");
Enter fullscreen mode Exit fullscreen mode

Search Params:

const [search, setSearch] = useSearchParams();
search.get("type");
setSearch({ type: "filtered" });
Enter fullscreen mode Exit fullscreen mode

Ideal for:

  • filters
  • sorting
  • pagination
  • tabs

πŸŽ‰ Final Thoughts

React Router Data APIs give you:

  • server-style data loading
  • easy mutations
  • caching & auto revalidation
  • nested layouts
  • streaming UIs
  • built-in loading states
  • per-route errors
  • intuitive search param handling
  • zero useEffect data fetching

This transforms React into a structured, predictable, scalable system for building real web applications.

Top comments (0)