Skip to main content
skill CX Codex

remix — Claude Code skill: Remix loaders, actions, routes

A skill that turns your coding agent into a Remix and TypeScript helper, with ready patterns for data loading, mutations and nested routes.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add Mindrally/skills --skill remix -a claude-code
Open on GitHub

What remix does

The skill sets the agent up as an expert in Remix, React and TypeScript. It ships rules and code samples: a basic loader with useLoaderData, a loader that checks the session and redirects, an action that reads request.formData(), validates fields and returns errors, plus useFetcher for non-navigation requests and optimistic UI.

Nested routing gets its own section. Layout routes with Outlet, pathless layouts such as _auth.tsx, resource routes with no default export, ErrorBoundary built on useRouteError and isRouteErrorResponse, MetaFunction for titles and LinksFunction for stylesheets. Sessions are covered too: createCookieSessionStorage, httpOnly, sameSite: 'lax' and a secret pulled from process.env.SESSION_SECRET.

The project layout is spelled out as an app/ tree with routes/, models/, utils/, entry.client.tsx, entry.server.tsx and root.tsx. Performance notes close it out: prefetch="intent" on links, defer for streaming, dynamic imports for code splitting. If your app is not on Remix, the agent will still push you toward this architecture, so think twice before adding it to a non-Remix repo.

What it helps with

  • write a loader that fetches a record by route param
  • handle form submission in an action and return validation errors
  • build a nested layout with Outlet and a pathless route
  • add optimistic delete with useFetcher
  • set up a cookie session with httpOnly and a secret

Example prompts for the agent

  • Create routes/posts/$slug.tsx with a loader that fetches the post by slug and throws a 404 when it is missing
  • Write an action for the new-post form that validates the title field and redirects to the post page
  • Show me how to build a like button with useFetcher that does not navigate away

How to install remix

1Skills CLI (Vercel)

npx skills add Mindrally/skills --skill remix -a claude-code

2Manually: put the files in place

~/.claude/skills/remix/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/remix/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about remix

How do I install remix in Claude?

Run npx skills add Mindrally/skills --skill remix -a claude-code. The skill file lands at ~/.claude/skills/remix/SKILL.md.

What does the remix skill do?

It gives the agent Remix development guidance: loader and action examples, nested routes, useFetcher, error boundaries and TypeScript session handling.

Which platforms support the remix skill?

It installs into Claude, Codex, Cursor, GitHub Copilot, Gemini CLI and OpenCode, each with its own install command.

Source text

SKILL.md · Apache-2.0
---
name: remix
description: Expert guidance for Remix development with TypeScript, loaders/actions, nested routes, and full-stack web application best practices
metadata:
  maintainer: Mindrally
  source: https://github.com/Mindrally/skills
---

# 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<typeof loader>();

  return <article>{post.content}</article>;
}
```

### 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<string, string> = {};

  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<typeof action>();

  return (
    <Form method="post">
      <input name="title" type="text" />
      {actionData?.errors?.title && (
        <p className="error">{actionData.errors.title}</p>
      )}
      <textarea name="content" />
      <button type="submit">Create Post</button>
    </Form>
  );
}
```

## Nested Routes

### Layout Routes
```typescript
// routes/dashboard.tsx (layout)
import { Outlet } from '@remix-run/react';

export default function DashboardLayout() {
  return (
    <div className="dashboard">
      <nav>
        <Link to="/dashboard">Overview</Link>
        <Link to="/dashboard/settings">Settings</Link>
      </nav>
      <main>
        <Outlet />
      </main>
    </div>
  );
}
```

### Pathless Layouts
```typescript
// routes/_auth.tsx (pathless layout for /login, /register)
export default function AuthLayout() {
  return (
    <div className="auth-container">
      <Outlet />
    </div>
  );
}
```

## useFetcher

### Non-Navigation Fetches
```typescript
import { useFetcher } from '@remix-run/react';

export default function LikeButton({ postId }: { postId: string }) {
  const fetcher = useFetcher();
  const isLiking = fetcher.state !== 'idle';

  return (
    <fetcher.Form method="post" action="/api/like">
      <input type="hidden" name="postId" value={postId} />
      <button type="submit" disabled={isLiking}>
        {isLiking ? 'Liking...' : 'Like'}
      </button>
    </fetcher.Form>
  );
}
```

### Optimistic UI
```typescript
export default function TodoItem({ todo }: { todo: Todo }) {
  const fetcher = useFetcher();

  const isDeleting = fetcher.formData?.get('_action') === 'delete';

  if (isDeleting) {
    return null; // Optimistically remove
  }

  return (
    <li>
      {todo.title}
      <fetcher.Form method="post">
        <input type="hidden" name="_action" value="delete" />
        <input type="hidden" name="id" value={todo.id} />
        <button type="submit">Delete</button>
      </fetcher.Form>
    </li>
  );
}
```

## Error Boundaries

```typescript
import { useRouteError, isRouteErrorResponse } from '@remix-run/react';

export function ErrorBoundary() {
  const error = useRouteError();

  if (isRouteErrorResponse(error)) {
    return (
      <div>
        <h1>{error.status} {error.statusText}</h1>
        <p>{error.data}</p>
      </div>
    );
  }

  return (
    <div>
      <h1>Error</h1>
      <p>{error instanceof Error ? error.message : 'Unknown error'}</p>
    </div>
  );
}
```

## Resource Routes

```typescript
// 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

```typescript
import type { MetaFunction, LinksFunction } from '@remix-run/node';

export const meta: MetaFunction<typeof loader> = ({ data }) => {
  return [
    { title: data?.post.title ?? 'Blog' },
    { name: 'description', content: data?.post.excerpt },
  ];
};

export const links: LinksFunction = () => {
  return [
    { rel: 'stylesheet', href: styles },
    { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
  ];
};
```

## Session Management

```typescript
// 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

© Mindrally · Apache-2.0 · Mindrally/skills

Similar skills

Everything for Frontend and UI →

From the same set214

less-best-practices This skill guides coding assistants on Less architecture, modular project layout, variable scales, and reusable mixins. Keep stylesheet specificity low and maintainable. CX Codex +1 692installs llamaindex-development Guides coding assistants in building Python RAG applications with LlamaIndex. Helps generate document parsers, vector indexes, and query engines. CX Codex +1 691installs meta-prompt A skill with meta-prompting templates that critique model responses, walk through a solution path and give it a score. Handy when you need to check answer quality. CX Codex +1 691installs cloudflare-development A skill that hands your agent Cloudflare edge practices: Workers, Pages, KV, D1, R2 and Durable Objects. Useful when the agent writes worker code or edits wrangler.toml. CX Codex +1 689installs salesforce-dx A skill that makes your coding agent follow Salesforce DX conventions: Apex, triggers, LWC, scratch orgs and SFDX CLI workflows. CX Codex +1 688installs micronaut Tells the agent how to write Micronaut code: compile-time DI, GraalVM native builds, reactive services. Useful when your Java project should follow the framework instead of generic advice. CX Codex +1 687installs julia Turns the agent into a Julia-aware pair programmer: multiple dispatch, type stability, docstrings and tests. Useful for numerical and scientific code. CX Codex +1 687installs clerk-authentication Rules for wiring Clerk into Next.js: middleware, hooks, protected server actions and API routes. Useful once your app already runs on Clerk. CX Codex +1 683installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor