Most React frameworks fight the web platform. Remix embraces it. Forms, HTTP caching, progressive enhancement — all built on web standards.
What is Remix?
Remix is a full-stack React framework focused on web fundamentals. Now part of React Router v7, it uses the web platform's native features instead of reinventing them with JavaScript.
Why Remix Thinks Differently
1. Nested Routes with Parallel Data Loading
app/routes/
├── _index.tsx → /
├── dashboard.tsx → /dashboard (layout)
├── dashboard._index.tsx → /dashboard (index)
├── dashboard.settings.tsx → /dashboard/settings
└── blog.$slug.tsx → /blog/:slug
// dashboard.tsx — layout route
export async function loader({ request }: LoaderFunctionArgs) {
const user = await getUser(request);
return json({ user });
}
export default function Dashboard() {
const { user } = useLoaderData<typeof loader>();
return (
<div>
<nav>Welcome, {user.name}</nav>
<Outlet /> {/* Child routes render here */}
</div>
);
}
Parent and child loaders run in parallel. No waterfall fetches.
2. Form Handling via Web Standards
// app/routes/contact.tsx
export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const email = formData.get('email');
const message = formData.get('message');
await sendEmail({ email, message });
return redirect('/thank-you');
}
export default function Contact() {
const navigation = useNavigation();
const isSubmitting = navigation.state === 'submitting';
return (
<Form method="post">
<input type="email" name="email" required />
<textarea name="message" required />
<button disabled={isSubmitting}>
{isSubmitting ? 'Sending...' : 'Send'}
</button>
</Form>
);
}
Works without JavaScript. Progressive enhancement by default.
3. Error Boundaries at Every Route
export function ErrorBoundary() {
const error = useRouteError();
if (isRouteErrorResponse(error)) {
return <div>{error.status}: {error.statusText}</div>;
}
return <div>Something went wrong</div>;
}
A broken child route doesn't crash the parent. Users see errors in context.
4. HTTP Caching Built In
export async function loader({ request }: LoaderFunctionArgs) {
const products = await db.products.findMany();
return json(products, {
headers: {
'Cache-Control': 'public, max-age=300, s-maxage=3600',
},
});
}
Use standard HTTP caching instead of client-side caching libraries.
5. Optimistic UI
export default function TodoList() {
const todos = useLoaderData<typeof loader>();
const fetchers = useFetchers();
// Include optimistic todos
const optimisticTodos = fetchers
.filter(f => f.formData)
.map(f => ({
title: f.formData.get('title'),
completed: false,
optimistic: true,
}));
return (
<ul>
{[...todos, ...optimisticTodos].map(todo => (
<li key={todo.id} style={{ opacity: todo.optimistic ? 0.5 : 1 }}>
{todo.title}
</li>
))}
</ul>
);
}
Remix vs Next.js
| Remix / React Router v7 | Next.js | |
|---|---|---|
| Data loading | Parallel (nested routes) | Sequential (by default) |
| Forms | Web standard <Form>
|
Server actions |
| Caching | HTTP headers | Custom cache |
| Error handling | Per-route boundaries | Error.tsx |
| Progressive enhancement | Core principle | Optional |
| Deployment | Anywhere | Vercel-optimized |
Getting Started
npx create-remix@latest
cd my-remix-app
npm run dev
The Bottom Line
Remix proves that web standards aren't limitations — they're superpowers. Parallel data loading, progressive forms, HTTP caching, and error isolation make apps faster and more resilient.
Need data solutions? I build web scraping tools. Check my Apify actors or email spinov001@gmail.com.
Top comments (0)