Loading...
Loading...
Best practices for Remix (2025-2026 Edition), focusing on React Router v7 migration, server-first data patterns, and error handling.
npx skill4agent add toilahuongg/google-antigravity-kit remixjs-best-practicesremix.config.jsvite.config.tsnpx codemod remix/2/react-router/upgradeuseEffect// ✅ Good: Typed loader with single strict return
export const loader = async ({ request }: LoaderFunctionArgs) => {
const user = await getUser(request);
if (!user) throw new Response("Unauthorized", { status: 401 });
return json({ user });
};
// Component gets fully typed data
export default function Dashboard() {
const { user } = useLoaderData<typeof loader>();
return <h1>Hello, {user.name}</h1>;
}onClick<Form>// ✅ Good: Descriptive, declarative mutation
<Form method="post" action="/update-profile">
<button type="submit">Save</button>
</Form>// routes/dashboard.tsx (Nested Route)
export function ErrorBoundary() {
const error = useRouteError();
return <div className="p-4 bg-red-50">Widget crashed: {error.message}</div>;
}throw new Response(...)// Action
if (name.length < 3) {
return json({ errors: { name: "Too short" } }, { status: 400 });
}
// Component
const actionData = useActionData<typeof action>();
{actionData?.errors?.name && <span>{actionData.errors.name}</span>}Cache-Controlexport const loader = async () => {
return json(data, {
headers: { "Cache-Control": "public, max-age=3600" }
});
};deferexport const loader = async () => {
const critical = await getCriticalData();
const slow = getSlowData(); // Promise
return defer({ critical, slow });
};
// UI supports <Suspense> for the slow part