In short
Remix bet on plain web basics: forms that work without JavaScript, data loaded per route, and pages that refresh themselves after a write. Those ideas now live on inside React Router v7.
A full-stack React framework emphasizing web fundamentals (nested routing, progressive enhancement, loaders/actions tightly coupled to routes) — a notable alternative to Next.js with a different philosophy.

Think of it like this
Every coach loads at once
At a busy station, tiffins for coaches A, B and C are loaded by different helpers at the same time, instead of one person walking the whole train. Remix's nested routes work like that: each part of the page has its own loader, and they all run in parallel.
Watch it happen
Loaders read, actions write
GET /invoices/7
1/7The URL matches two nested routes: the invoices layout and the invoice 7 page.
The big ideas
What you need to know
Loaders read
Each route exports a loader that runs on the server before rendering and returns that route's data.
Actions write
Each route can export an action that handles its own form posts. No separate API file needed.
Nested routes
Layouts and child pages each own their data, and their loaders run in parallel instead of one after another.
Works without JS
<Form> falls back to a real HTML form post, so the core flow still works if JavaScript fails to load.
See it in code
Walk through the code
1// app/routes/notes.tsx2import { Form } from "react-router";3import type { Route } from "./+types/notes";4 5export async function loader() {6 return { notes: await db.note.findMany() };7}8export async function action({ request }: Route.ActionArgs) {9 const form = await request.formData();10 await db.note.create({ data: { text: String(form.get("text")) } });11 return null;12}13export default function Notes({ loaderData }: Route.ComponentProps) {14 const list = loaderData.notes.map((n) => <li key={n.id}>{n.text}</li>);15 return <><ul>{list}</ul><Form method="post"><input name="text" /><button>Add</button></Form></>;16}1/4The loader runs on the server for GET requests and returns this route's data.
This is React Router v7 framework mode, where Remix's ideas now live. The ./+types import is generated for you and types loaderData.
Save yourself the pain
Common mistakes
Oops
Fetching page data in useEffect inside a Remix route.
Do this instead
Put it in the route's loader. It runs on the server before render, so the HTML arrives with data.
Oops
Manually refetching the list after a form submit.
Do this instead
Return from the action and let the router revalidate loaders. The list updates by itself.
Your mission
A notes page the Remix way
Quick check
After a Remix action saves a new note, how does the notes list update?
Go deeper
- 1Read Remix's loader/action model and compare it to Next.js's data-fetching approach.
- 2Understand Remix's emphasis on working well even with JavaScript disabled.
- 3Recognize the core full-stack concepts transfer between these frameworks even if the syntax differs.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.