Remix v2 gives you a full-stack framework with built-in data loading, mutations, and server-side rendering.
Why Remix v2?
- Nested routes: Each route is a boundary with its own data and error handling
- Server-first: Data loads on the server, HTML streams to client
- Progressive enhancement: Forms work without JavaScript
- Web Fetch API: Standard Request/Response everywhere
- Vite-powered: Lightning-fast HMR and builds
Quick Setup
npx create-remix@latest my-app
cd my-app && npm run dev
Route-Based Data Loading
Every route exports a loader that runs on the server:
import { json } from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';
export async function loader() {
const posts = await db.post.findMany({ orderBy: { createdAt: 'desc' }, take: 20 });
return json({ posts });
}
export default function Posts() {
const { posts } = useLoaderData<typeof loader>();
return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
}
Mutations with Actions
import { redirect } from '@remix-run/node';
import { Form } from '@remix-run/react';
export async function action({ request }) {
const formData = await request.formData();
await db.post.create({ data: { title: formData.get('title'), body: formData.get('body') } });
return redirect('/posts');
}
export default function NewPost() {
return (
<Form method="post">
<input name="title" />
<textarea name="body" />
<button type="submit">Create</button>
</Form>
);
}
Nested Routes
app/routes/
_index.tsx -> /
posts.tsx -> /posts layout
posts._index.tsx -> /posts index
posts.$id.tsx -> /posts/:id
Streaming with defer
import { defer } from '@remix-run/node';
import { Await, useLoaderData } from '@remix-run/react';
import { Suspense } from 'react';
export async function loader() {
return defer({ fast: await getFastData(), slow: getSlowData() });
}
export default function Page() {
const { fast, slow } = useLoaderData<typeof loader>();
return (
<div>
<h1>{fast.title}</h1>
<Suspense fallback={<p>Loading...</p>}>
<Await resolve={slow}>{data => <p>{data.content}</p>}</Await>
</Suspense>
</div>
);
}
Resource Routes for APIs
export async function loader({ request }) {
const url = new URL(request.url);
const posts = await db.post.findMany({ where: { title: { contains: url.searchParams.get('q') } } });
return json(posts);
}
Real-World Use Case
A SaaS team migrated from Next.js to Remix v2. Bundle size dropped 40%, client-side waterfalls eliminated, and forms worked without JS.
Need to automate data collection for YOUR project? Check out my Apify actors for ready-made scrapers, or email spinov001@gmail.com for custom solutions.
Top comments (0)