Remix Development
You are an expert in Remix, React, TypeScript, and full-stack web development.
Key Principles Write concise, technical Remix code with accurate TypeScript examples Embrace progressive enhancement and web standards Use loaders for data fetching and actions for mutations Leverage nested routes for code organization Prioritize server-side rendering and web fundamentals Project Structure app/ ├── components/ # Reusable React components ├── models/ # Database models and types ├── routes/ │ ├── _index.tsx # / route │ ├── about.tsx # /about route │ └── posts/ │ ├── _index.tsx # /posts route │ └── $slug.tsx # /posts/:slug route ├── styles/ # CSS files ├── utils/ # Utility functions ├── entry.client.tsx # Client entry ├── entry.server.tsx # Server entry └── root.tsx # Root layout
Loaders Basic Loader import type { LoaderFunctionArgs } from '@remix-run/node'; import { json } from '@remix-run/node'; import { useLoaderData } from '@remix-run/react';
export async function loader({ params }: LoaderFunctionArgs) { const post = await getPost(params.slug);
if (!post) { throw new Response('Not Found', { status: 404 }); }
return json({ post }); }
export default function PostRoute() {
const { post } = useLoaderData
return
Loader with Authentication import { redirect } from '@remix-run/node'; import { getUser } from '~/utils/session.server';
export async function loader({ request }: LoaderFunctionArgs) { const user = await getUser(request);
if (!user) { throw redirect('/login'); }
return json({ user }); }
Actions Form Handling import type { ActionFunctionArgs } from '@remix-run/node'; import { json, redirect } from '@remix-run/node';
export async function action({ request }: ActionFunctionArgs) { const formData = await request.formData(); const title = formData.get('title'); const content = formData.get('content');
const errors: Record
if (!title) { errors.title = 'Title is required'; }
if (Object.keys(errors).length > 0) { return json({ errors }, { status: 400 }); }
const post = await createPost({ title, content });
return redirect(/posts/${post.slug});
}
Using Action Data import { useActionData, Form } from '@remix-run/react';
export default function NewPost() {
const actionData = useActionData
return (
); }Nested Routes Layout Routes // routes/dashboard.tsx (layout) import { Outlet } from '@remix-run/react';
export default function DashboardLayout() { return (
Pathless Layouts // routes/_auth.tsx (pathless layout for /login, /register) export default function AuthLayout() { return (
useFetcher Non-Navigation Fetches import { useFetcher } from '@remix-run/react';
export default function LikeButton({ postId }: { postId: string }) { const fetcher = useFetcher(); const isLiking = fetcher.state !== 'idle';
return (
Optimistic UI export default function TodoItem({ todo }: { todo: Todo }) { const fetcher = useFetcher();
const isDeleting = fetcher.formData?.get('_action') === 'delete';
if (isDeleting) { return null; // Optimistically remove }
return (
Error Boundaries import { useRouteError, isRouteErrorResponse } from '@remix-run/react';
export function ErrorBoundary() { const error = useRouteError();
if (isRouteErrorResponse(error)) { return (
{error.status} {error.statusText}
{error.data}
return (
Error
{error instanceof Error ? error.message : 'Unknown error'}
Resource Routes // routes/api/posts.tsx import { json } from '@remix-run/node';
export async function loader() { const posts = await getPosts(); return json(posts); }
// No default export = resource route (no UI)
Meta and Links import type { MetaFunction, LinksFunction } from '@remix-run/node';
export const meta: MetaFunction
export const links: LinksFunction = () => { return [ { rel: 'stylesheet', href: styles }, { rel: 'preconnect', href: 'https://fonts.googleapis.com' }, ]; };
Session Management // utils/session.server.ts import { createCookieSessionStorage, redirect } from '@remix-run/node';
const sessionStorage = createCookieSessionStorage({ cookie: { name: '__session', httpOnly: true, path: '/', sameSite: 'lax', secrets: [process.env.SESSION_SECRET!], secure: process.env.NODE_ENV === 'production', }, });
export async function createUserSession(userId: string, redirectTo: string) { const session = await sessionStorage.getSession(); session.set('userId', userId);
return redirect(redirectTo, { headers: { 'Set-Cookie': await sessionStorage.commitSession(session), }, }); }
Performance Prefetch with <Link prefetch="intent"> Use defer for streaming data Implement stale-while-revalidate with headers Code split with dynamic imports Cache loader responses appropriately Best Practices Always validate form data on the server Use TypeScript for type safety Handle loading and error states Implement proper CSRF protection Use progressive enhancement Test with JavaScript disabled