--- name: remix description: Expert guidance for Remix development with TypeScript, loaders/actions, nested routes, and full-stack web application best practices --- # 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 ```typescript 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
{post.content}
; } ``` ### Loader with Authentication ```typescript 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 ```typescript 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 ```typescript import { useActionData, Form } from '@remix-run/react'; export default function NewPost() { const actionData = useActionData(); return (
{actionData?.errors?.title && (

{actionData.errors.title}

)}