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
🔹 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>
</>
);
}
🧠 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);
}
✔ 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;
✔ No unnecessary API calls
✔ Store hydrated after first load
⭐ 10. Avoiding Re-Fetch on Revisit
Use this:
export function shouldRevalidate() {
return false;
}
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>
⭐ 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:
- Loaders
- Actions
- entry.server.js
- React SSR renderer
- Route manifest
✔ Client bundle
Contains:
- Route components
- useLoaderData / useActionData
- event handlers
- UI logic
- 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)