Remix Cheat Sheet

Quick reference for Remix — loaders, actions, forms, nested routing, error boundaries, and deployment. Everything you need to build full-stack React apps with Remix.

Project Setup File-Based Routing Loaders (GET Data) Actions (POST/PUT/DELETE) Forms & Navigation Error Handling Meta & Headers

Project Setup

npx create-remix@latest Create new Remix project
npx remix dev Start dev server with HMR
npx remix build Build for production
npx remix-serve build/server/index.js Serve production build
remix.config.js Main configuration file
npx remix routes Print all application routes

File-Based Routing

app/routes/_index.tsx → / (root index route)
app/routes/about.tsx → /about
app/routes/blog.$slug.tsx → /blog/:slug (dynamic param)
app/routes/blog_.tsx → /blog (pathless layout escape)
app/routes/($lang).about.tsx → /about or /:lang/about (optional param)
app/routes/$.tsx → /* (splat / catch-all route)
app/routes/_auth.login.tsx → /login (layout group, _auth is pathless)
app/routes/blog._index.tsx → /blog (index of nested layout)
app/root.tsx Root layout (wraps everything)

Loaders (GET Data)

export async function loader({ request, params }) { ... } Server-side data loading (GET)
return json({ posts }) Return JSON response from loader
return json({ error }, { status: 404 }) Return with custom status code
return redirect("/login") Redirect from loader
const data = useLoaderData() Access loader data in component
const url = new URL(request.url) Parse URL/search params in loader
url.searchParams.get("q") Read query parameters
throw json({ message: "Not found" }, 404) Throw response (caught by error boundary)

Actions (POST/PUT/DELETE)

export async function action({ request }) { ... } Handle form submissions (non-GET)
const formData = await request.formData() Parse form data from request
formData.get("email") Get single form field value
return json({ errors: { email: "Required" } }) Return validation errors
const actionData = useActionData() Access action response in component
return redirect("/dashboard") Redirect after successful action
const intent = formData.get("intent") Handle multiple buttons/intents per form

Forms & Navigation

Progressive enhancement form (replaces )
GET form (search, filters)
const navigation = useNavigation() Track navigation/submission state
navigation.state === "submitting" Check if form is submitting
navigation.state === "loading" Check if page is loading
const fetcher = useFetcher() Submit without navigation
Inline form that does not navigate
fetcher.submit(formData, { method: "post" }) Programmatic submission
const submit = useSubmit() Imperative form submission hook

Error Handling

export function ErrorBoundary() { ... } Route-level error boundary
const error = useRouteError() Access caught error
isRouteErrorResponse(error) Check if error is a Response (thrown json/redirect)
error.status, error.data Access Response error status and data
export function HydrateFallback() { ... } Loading UI while route hydrates (SPA mode)
export const handle = { breadcrumb: "Blog" } Route handle for useMatches()
const matches = useMatches() Get all matched routes (breadcrumbs, meta)

Meta & Headers

export const meta: MetaFunction = () => [ { title: "Page" } ] Set page title
{ name: "description", content: "..." } Set meta description
{ property: "og:title", content: "..." } Set Open Graph meta
export function headers({ loaderHeaders }) { ... } Set HTTP response headers
export function links() { return [...] } Add tags (stylesheets, prefetch)
Prefetch on hover/focus intent

Try It Live

Test these patterns with our free API Tester. No signup needed.

Open API Tester →
Step-by-Step Guide

How to Test an API Online

Read Guide →

More Cheat Sheets