DEV Community

Cover image for React Router 7 Framework Mode — A Crisp Beginner-Friendly Guide (2025 Edition)
Vishwark
Vishwark

Posted on

React Router 7 Framework Mode — A Crisp Beginner-Friendly Guide (2025 Edition)

A continuation of my previous post: “React Router Data APIs — The Complete Beginner-Friendly Guide (2025 Edition)”

React Router 7 brings Framework Mode, turning the router into a full SSR framework—simple, predictable, and perfect for app-style UIs.

This guide is intentionally short, structured, and builds the mental model step-by-step.


⭐ 1. File Structure (Start Here — The Mental Model Foundation)

Framework Mode uses a Remix-style folder structure:

app/
  routes/
    index.tsx
    dashboard.tsx
    dashboard.profile.tsx
    products.$id.tsx
  entry.client.tsx
  entry.server.tsx
Enter fullscreen mode Exit fullscreen mode

🔹 routes/*

Contains route files → each file = one route.

🔹 entry.client.tsx

Hydration + SPA navigation.

🔹 entry.server.tsx

SSR rendering, loader execution, streaming.

This structure builds the mental model that:

  • UI lives in route files
  • Loaders/actions run in server bundle
  • Hydration & navigation handled by client entry

This is RR7’s foundation.


⭐ 2. What Goes Inside a Route File? (Super Important)

Each route file can export three things:

✔ 1. A default React component (client-side UI)

✔ 2. A loader (server-side GET logic)

✔ 3. An action (server-side POST/PUT/DELETE logic)

Here’s the simplest real-world example:

📄 app/routes/products.$id.tsx

// 1. Loader (server)
export async function loader({ params, request }) {
  const token = request.headers.get("cookie");
  return fetchProduct(params.id, token);
}

// 2. Action (server)
export async function action({ request, params }) {
  const form = await request.formData();
  const qty = form.get("qty");
  await updateCart(params.id, qty);
  return redirect("/cart");
}

// 3. Component (client)
export default function ProductPage() {
  const product = useLoaderData();

  return (
    <>
      <h1>{product.name}</h1>
      <Form method="post">
        <input name="qty" defaultValue={1} />
        <button>Add to Cart</button>
      </Form>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

🧠 Mental model:

  • Loader → server-only fetch
  • Action → server-only mutation
  • Component → client UI
  • All in ONE file → extremely simple

⭐ 3. When to Use Loaders (Server-Side Fetching)

Use loaders when:

  • Page needs SSR
  • Data depends on cookies
  • Protected API endpoints
  • Page-level fetch
  • Navigation-based fetch
  • Product/blog details
  • Dashboard pages
  • SEO-critical pages

Good examples:

  • /products/:id
  • /blog/:slug
  • /dashboard
  • /orders/:orderId

If it’s page data → use a loader.


⭐ 4. When NOT to Use Loaders (Use useEffect Instead)

Avoid loaders for browser-driven UI logic, like:

  • localStorage / sessionStorage
  • notifications
  • geolocation
  • window/document
  • theme switching
  • input focus
  • event listeners
  • debounced search
  • auto-complete
  • infinite scroll
  • background fetch

If it’s UI interaction → use useEffect.


⭐ 5. Loaders vs Effects (Golden Rule)

Loaders (Server) Effects (Client UI)
SSR data Browser APIs
Cookies-based APIs Theme switching
Protected routes Infinite scroll
Navigation fetch Focus input
Page HTML data Event listeners
SEO UI-only fetch

Loaders = server data → SSR
Effects = browser behavior → UI


⭐ 6. Loaders in Data Mode vs Framework Mode

Mode Loader Runs In Bundling
Data Mode Client Vite/Webpack
Framework Mode Server RR7 compiler

Framework Mode = SSR-first.
Data Mode = SPA-first.


⭐ 7. Handling Auth with Loaders (Server)

Because loaders run on the server:

export async function loader({ request }) {
  const cookie = request.headers.get("cookie");
  if (!cookie) throw redirect("/login");
  return getUser(cookie);
}
Enter fullscreen mode Exit fullscreen mode

✔ Can read cookies
✔ Works on SSR + navigation
✔ Token never exposed to browser


⭐ 8. Things to Avoid in Framework Mode

❌ Using localStorage in loader
❌ Using window/document in loader
❌ Fetching protected API in useEffect
❌ Client-side re-fetching when loader can
❌ Heavy UI data fetch inside effect
❌ Mixing UI & data responsibilities

Framework Mode is server-first for data, client for UI.


⭐ 9. Loaders → How They Update Client Stores (Zustand/Redux)

Pattern:

const data = useLoaderData();
const cached = useStore(s => s.items);

useEffect(() => {
  if (!cached) setItems(data);
}, [data, cached]);

return cached ?? data;
Enter fullscreen mode Exit fullscreen mode

✔ No unnecessary API calls

✔ Store hydrated after first load


⭐ 10. Avoiding Re-Fetch on Revisit

Use this:

export function shouldRevalidate() {
  return false;
}
Enter fullscreen mode Exit fullscreen mode

Perfect for cached dashboards.


⭐ 11. Actions — Server Mutations Only

Use actions for:

  • login
  • signup
  • update profile
  • delete item
  • add to cart
  • any form submission

Actions can:

  • return data
  • redirect
  • throw errors

After action finishes → loaders re-run automatically.


⭐ 12. Fetchers — Inline Updates Without Navigation

Use fetchers when:

  • No navigation required
  • Inline UI updates
  • AJAX-like behavior
  • Optimistic UI patterns
const fetcher = useFetcher();

<fetcher.Form method="post">
  <input name="comment" />
  <button>Submit</button>
</fetcher.Form>
Enter fullscreen mode Exit fullscreen mode

⭐ 13. Routing Types in RR7

RR7 supports:

  • Static routes
  • Dynamic routes (products.$id)
  • Nested routes (dashboard.profile)
  • Index routes (index.tsx)
  • Grouped routes ((auth)/login.tsx)
  • Parallel routes (dashboard@sidebar.tsx)
  • Intercepting/modal routes

Use <Outlet /> for nested layouts.


⭐ 14. Bundling in Framework Mode (Two Separate Outputs)

npm run build creates two bundles:

✔ Server bundle

Contains:

  1. Loaders
  2. Actions
  3. entry.server.js
  4. React SSR renderer
  5. Route manifest

✔ Client bundle

Contains:

  1. Route components
  2. useLoaderData / useActionData
  3. event handlers
  4. UI logic
  5. entry.client.js

Server bundle never ships to browser.


⭐ 15. Server Responsibilities

The server handles:

  • loader execution
  • action execution
  • SSR + streaming
  • generating HTML
  • protected logic
  • route-based SSR

Requires:

  • Node / Bun / Cloudflare Worker
  • Express / Fastify (optional)
  • Or proxy via Nginx

⭐ 16. Client Responsibilities

Client handles:

  • hydration
  • SPA navigation
  • useEffect logic
  • browser APIs
  • state management
  • event listeners
  • animations & UI
  • inline fetchers

⭐ Final Mental Model Summary

📌 Loaders → Server data (SSR + navigation)

📌 Actions → Server mutations

📌 fetcher → Inline submit, no navigation

📌 Components → Pure client-side React

📌 useEffect → UI-only logic

📌 Server bundle → secure business logic

📌 Client bundle → UI + interactivity

React Router 7 Framework Mode gives you the simplest, cleanest full-stack React model available today—no RSC complexity, no use-client boundaries, no cache magic rules.

Just React + predictable server data + SSR.


Top comments (0)