Agent SkillsTanStack/ai › remix

remix

GitHub

用于构建和审查Remix 3应用,涵盖路由、控制器、中间件、UI组件及测试等全栈开发任务。

examples/ts-remix-chat/.agents/skills/remix/SKILL.md TanStack/ai

Trigger Scenarios

需要构建或重构Remix应用结构 审查Remix代码正确性与架构

Install

npx skills add TanStack/ai --skill remix -g -y
More Options

Non-standard path

npx skills add https://github.com/TanStack/ai/tree/main/examples/ts-remix-chat/.agents/skills/remix -g -y

Use without installing

npx skills use TanStack/ai@remix

指定 Agent (Claude Code)

npx skills add TanStack/ai --skill remix -a claude-code -g -y

安装 repo 全部 skill

npx skills add TanStack/ai --all -g -y

预览 repo 内 skill

npx skills add TanStack/ai --list

SKILL.md

Frontmatter
{
    "name": "remix",
    "description": "Build and review Remix 3 applications using the `remix` npm package and subpath imports. Use when working on Remix app structure, routes, controllers, middleware, validation, data access, auth, sessions, file uploads, server setup, UI components, hydration, HMR, navigation, or tests."
}

Build a Remix App

Use this skill for end-to-end Remix app work. This skill helps you choose the right layer first, reach for the right package, and avoid the most common Remix-specific mistakes.

Full Package Documentation

This skill is the quick guide. When you need fuller API documentation, examples, or package-specific details for a remix/* subpath, first look for a README next to the relevant generated source file in the published remix package: node_modules/remix/src/<subpath>/README.md. These published README files are generated mirrors; in the Remix source repository, the canonical README lives in the owning packages/* package and the packages/remix/src/**/README.md mirrors are intentionally ignored. If that README does not exist, look for the nearest parent README because some subpaths share their parent package documentation.

Examples:

  • remix/router -> node_modules/remix/src/fetch-router/README.md
  • remix/ui/button -> node_modules/remix/src/ui/button/README.md

What Remix Is

Remix 3 is a server-first web framework built on Web APIs such as Request, Response, URL, and FormData. All packages ship from a single npm package, remix, and are imported via subpath. There is no top-level remix import.

A Remix app has four main pieces:

  • Routes in app/routes.ts define the typed URL contract and power href() generation.
  • Controllers in app/actions implement that contract and return Response objects.
  • Middleware composes request lifecycle behavior and populates typed context via context.set(Key, value).
  • Components render UI with remix/ui. This is not React. A component receives a handle, reads current props from handle.props, and returns a zero-argument render function.

When To Use This Skill

Use this skill for:

  • new features or refactors that touch routing, controllers, middleware, data, auth, sessions, UI, or tests
  • reviewing Remix app code for correctness, architecture, or framework usage
  • answering "how should this be structured in Remix?" questions
  • finding the right package, reference doc, or default pattern for a task

Load Only The References You Need

Classify the task first, then load the smallest useful reference set. Each reference file starts with a "What This Covers" section that lists the topics inside it — read that first to confirm the file is relevant before reading the rest.

Use the table below to find candidates. Loading more than two or three files at once is usually a sign that the task hasn't been narrowed enough yet.

Task involves... Start with
Defining URLs, writing controllers and actions, returning responses references/routing-and-controllers.md
Composing the request lifecycle, ordering middleware, bridging to a server, development HMR references/middleware-and-server.md
Compiling and serving browser modules, asset URL namespaces, preloads, browser HMR references/assets-and-browser-modules.md
Parsing input, validating with schemas, defining tables, querying, migrations references/data-and-validation.md
Per-browser state, login flows, route protection, identity references/auth-and-sessions.md
Component setup, state, lifecycle, updates, queueTask, context references/component-model.md
Event handlers, styles, refs, click/key behavior, simple animations references/mixins-styling-events.md
clientEntry, run, <Frame>, navigation, browser HMR update handling, <head> references/hydration-frames-navigation.md
Router tests, component tests, test isolation references/testing-patterns.md
Spring physics, tweens, layout transitions references/animate-elements.md
Authoring custom reusable mixins references/create-mixins.md

Common bundles:

  • Form or CRUD feature -> routing, data and validation, testing; add auth if user-specific
  • Protected area -> auth and sessions, routing, testing
  • Interactive widget -> component model, mixins and styling; add hydration only if it runs in the browser
  • Browser asset pipeline -> assets and browser modules, hydration, middleware and server
  • Development HMR -> middleware and server, assets and browser modules, hydration
  • File upload -> middleware and server, data and validation, testing
  • Navigation or frames -> hydration, frames, navigation

Default Workflow

  1. Classify the change. Decide whether it changes the route contract, request lifecycle, data model, auth or session behavior, or only UI.
  2. Start from the server contract. Add or update app/routes.ts before wiring handlers or UI.
  3. Put code in the narrowest owner. Favor route-local code first, then promote only when reuse is real.
  4. Make the server path correct before adding browser behavior. A route should return the right Response via router.fetch(...) before you add clientEntry(...), animations, or DOM effects.
  5. Add middleware deliberately. Keep fast-exit middleware early and request-enriching middleware later. Export a typed AppContext from the middleware stack and use it in controllers.
  6. Validate input at the boundary. Parse and validate Request, FormData, params, cookies, and external payloads before they reach rendering or persistence logic.
  7. Hydrate only when necessary. Prefer server-rendered UI. Use clientEntry(...) and run(...) only for real browser interactivity or browser-only APIs.
  8. Test the narrowest meaningful layer. Prefer router tests for route behavior. Use component tests when the behavior is truly interactive or DOM-specific.
  9. Finish with verification. Re-read the route flow, confirm auth and authorization boundaries, and run the smallest relevant test and typecheck loop.

Project Layout

Use these root directories consistently:

  • app/ for runtime application code
  • db/ for migrations and local database files
  • root public/ for static assets served as-is from the app root
  • test/ for shared helpers, fixtures, and integration coverage
  • tmp/ for uploads, caches, local session files, and other scratch data

Inside app/, organize by responsibility:

  • actions/ for controller-owned route handlers, route-local response rendering, and route-local UI/helpers that are not shared across route areas
  • data/ for schema, queries, persistence setup, migrations, and runtime data initialization
  • middleware/ for request lifecycle concerns such as auth, sessions, uploads, and database injection
  • public/ directories inside the narrowest owner for browser-reachable source code, with the browser runtime entrypoint at app/actions/public/entry.ts
  • ui/ for shared cross-route UI primitives
  • utils/ only for genuinely cross-layer helpers that do not clearly belong elsewhere
  • routes.ts for the shared server-and-browser route contract and type-safe href generation
  • router.ts for router setup and wiring

Placement Precedence

When code could live in multiple places:

  1. Put it in the narrowest owner first.
  2. If it belongs to one route, keep it with that route.
  3. If it is shared UI across route areas, move it to app/ui/.
  4. If it is request lifecycle setup, keep it in app/middleware/.
  5. If it is schema, query, persistence, or startup data logic, keep it in app/data/.
  6. Use app/utils/ only as a last resort for truly cross-layer helpers.

Route Ownership

  • Put top-level leaf actions in app/actions/controller.tsx
  • A controller's actions object contains only direct leaf route keys from the route map passed to router.map(...)
  • Add app/actions/<route-key>/controller.tsx for each nested route map that needs actions or controller middleware, and map it explicitly with router.map(routes.<routeKey>, controller)
  • Name directories under app/actions/ after route-map keys, not URL path segments
  • Keep route-local UI and helpers next to the controller that owns them
  • Move shared cross-route UI to app/ui/
  • If a top-level leaf grows into a route map, move its handler into the nested route-key controller and update app/router.ts to map that route map explicitly

Response Rendering And Utilities

  • Install render() from remix/middleware/render in the router middleware stack for normal Remix UI applications. Pass render({ assets }) when source-based clientEntry() modules need browser URLs
  • Render UI responses at the action boundary with context.render(node, init). Status, headers, and other response policy remain explicit in the action
  • Use renderWith(...), renderToStream(...), and createHtmlResponse(...) only when an application intentionally owns a custom renderer contract or replaces the standard UI response pipeline
  • Put pure support code in focused app/utils/<topic>.ts modules. Formatting, MIME classification, path parsing, sorting, and normalization should be testable without a router, request context, or Response, and should not import from app/actions, remix/ui/server, or remix/response/*
  • Do not introduce page-data intermediary shapes only to keep route-specific renderers away from render(...); keep response assembly in actions and extract only the pure helpers

Layout Anti-Patterns

  • Do not create app/lib/ as a generic dumping ground
  • Do not create app/components/ as a second shared UI bucket when app/ui/ already owns that role
  • Do not create app/controllers/; Remix app route handlers live under app/actions/
  • Do not put shared cross-route UI in app/actions/
  • Do not create standalone root action files; put root route actions in app/actions/controller.tsx
  • Do not put nested route-map keys in a controller's actions
  • Do not register normal app leaf routes directly in app/router.ts when they belong in a controller
  • Do not rely on controller middleware from one controller to protect another controller; add controller middleware explicitly in each controller that needs it
  • Do not put middleware or persistence helpers in app/utils/ when they have a clearer home

Core Remix Rules

  • Import from remix/<subpath>, never import { ... } from 'remix'
  • Treat app/routes.ts as the source of truth for URLs. Use routes.<name>.href(...) for redirects, links, tests, and internal URL construction
  • Controllers should return explicit Response objects, including redirects, 404s, and validation failures. At the route boundary, prefer returning a Response for expected outcomes (validation errors, conflicts, not found) over throwing for control flow
  • router.map(routes, controller) maps only the direct leaf routes in routes; nested route maps must be mapped with their own explicit controllers
  • Model HTTP behavior explicitly. Status codes, headers, redirects, cache rules, and content types are part of the route contract
  • Make the server route correct first. A POST should already return the right HTML, redirect, or error response on its own before clientEntry(...) layers interactivity on top
  • Validate input at the boundary using remix/data-schema (and remix/data-schema/form-data for forms). parseSafe makes the failure path a return value instead of an exception
  • Derive AppContext from the middleware stack so get(databaseContext), get(Session), get(Auth), and similar keys stay typed. If the controller never reads from context, it doesn't need the harness
  • Outside actions and controllers, only use getContext() when asyncContext() is in the middleware stack
  • Remix Component is not React: write function Name(handle: Handle<Props>) { return () => ... }, read props from handle.props, keep state in setup-scope variables, call handle.update() explicitly, and do DOM-sensitive work in event handlers or queueTask(...), not in render
  • Prefer host-element mixins via mix={mixin(...)} for behavior and styling instead of inventing custom host prop conventions. Use mix={[...]} only when composing multiple mixins
  • Keep short, one-off static styles inline with mix={css(...)}. Extract a module-scoped style descriptor when it forms a reused visual recipe, has substantial selectors, media queries, or keyframes, or is large enough to obscure the component. Export a style descriptor only after multiple modules need the same visual recipe; otherwise keep it with its narrowest owner
  • Hydrated clientEntry(...) props must be serializable. Do not pass functions, class instances, or opaque runtime objects

Security And Session Defaults

  • Never ship demo secrets. In non-test environments, require session and provider secrets from the environment and fail fast if they are missing
  • Use hardened cookies: httpOnly always, sameSite by default, and secure when serving over HTTPS
  • Regenerate session IDs on login, logout, and privilege changes
  • Use requireAuth() to protect authenticated route areas, but still authorize resource ownership inside handlers and data writes
  • Add CSRF protection when browser forms mutate state using cookie-backed sessions
  • Add CORS only for endpoints that must be called cross-origin. Prefer same-origin by default
  • Prefer JSX or remix/html-template for HTML generation so escaping stays correct
  • Validate uploads for size, type, and destination. Treat filenames and content as untrusted input

Testing Defaults

  • Prefer server and router tests first. Drive the app with router.fetch(new Request(...)) and assert on the returned Response
  • Keep controller tests shaped like controllers: root route behavior belongs in app/actions/controller.test.ts(x), and nested route-map behavior belongs beside that route-key controller
  • Build a fresh router per test or per suite so sessions, in-memory storage, and database state stay isolated
  • Use routes.<name>.href(...) in tests so URLs stay coupled to the route contract
  • For auth or session scenarios, use a test cookie and createMemorySessionStorage() instead of production storage
  • Co-locate tests for pure app/utils helpers beside their modules. Test response behavior through router or controller tests
  • Use component tests only for interactive or DOM-specific behavior. Render with createRoot(...), interact with the real DOM, and call root.flush() between steps
  • Prefer one representative behavior test over many repetitive assertion variants

Common Mistakes To Avoid

  • Treating Remix Component like React and reaching for hooks or implicit rerendering
  • Importing from a top-level remix entry instead of a subpath
  • Adding clientEntry(...) before the server-rendered route behavior is correct
  • Passing non-serializable props into clientEntry(...)
  • Calling getContext() without asyncContext() in the middleware stack
  • Getting middleware order wrong; fast exits like static files belong early, request enrichment later
  • Skipping boundary validation and trusting raw FormData, params, cookies, or external payloads
  • Letting route-local domain errors leak out of the controller. Translate expected outcomes (validation, conflicts, not-found) into the HTTP Response the route means to return rather than throwing a custom Error subclass and catching it elsewhere
  • Reaching for createCookie when a tamper-sensitive or server-managed per-browser fact really wants remix/session. If editing the value would be a bug, use a session
  • Building a JSON-only RPC layer when a normal form POST, redirect, or resource route would be simpler. Fetch-from-the-client is a layer on top of sound route behavior, not a replacement for it
  • Treating JSON state endpoints and <Frame> reloads as mutually exclusive patterns. Pick the lightest sync mechanism that fits the UX; small widgets may reasonably poll a JSON endpoint
  • Assuming authentication is enough without per-resource authorization checks
  • Dropping shared code into vague buckets like utils.ts, helpers.ts, or common.ts when ownership is known
  • Recreating the old app/controllers or standalone root action file layout instead of using controllers under app/actions
  • Putting nested route-map keys inside a controller actions object. Map nested route maps explicitly in app/router.ts
  • Treating direct router.get(...)/router.post(...) registrations as the default app structure instead of using controllers
  • Assuming controller middleware applies to controllers registered for nested route maps
  • Writing only component tests for a feature whose main behavior is really an HTTP route concern

Package Map

Use this map to find the right package quickly. Each entry says what the package is for, not just what it exports. Open the linked reference file when you need full examples.

Routing, Server, and Responses

  • remix/router — the router itself. Use for createRouter, controllers, middleware types, and registering routes
  • remix/routes — declarative route builders. Use for route, get, post, put, del, form, resources when defining app/routes.ts
  • remix/node-fetch-server — default Node adapter for new apps. Use createRequestListener with node:http, node:https, or node:http2 in server.ts when booting the template-style app
  • remix/node-hmr — optional development Node HMR runner for rapid UI edits. Use run in hmr.ts to supervise server.ts behind an hmr script, and use createHmrReadyFetch when a stable public proxy should wait for child server readiness during updates
  • remix/node-hmr/runtime — child-process runtime API for code running under remix/node-hmr. Use to create browser HMR channels for asset servers and to emit server readiness after the child server starts listening
  • remix/node-hmr/types — type-only entry for import.meta.hot in Node modules
  • remix/assets — browser asset server. Use for createAssetServer when serving compiled scripts and styles, getting public hrefs, emitting preloads, and wiring browser HMR. Configure a basePath; use optional directory mounts configuration when the default mounts that serve app at app and node_modules at npm are not enough; use allowFiles/denyFiles for path and glob rules; and use exact package names in allowPackages for package-level access. Shared compiler options such as target, sourceMaps, sourceMapSourcePaths, and minify live at the top level
  • remix/assets/types/hmr — type-only entry for import.meta.hot in browser modules compiled by remix/assets
  • remix/headersSuperHeaders plus typed header parsers and builders. Use the default export when you want a Headers subclass with typed accessors like headers.contentType, headers.cacheControl, and headers.setCookie; use named classes such as CacheControl, ContentDisposition, and Vary when working with individual header values
  • remix/response/redirectredirect(href, status?). Use for the canonical "POST then redirect" pattern and other location changes
  • remix/response/htmlcreateHtmlResponse. Use when you need an HTML Response from a string or stream without rendering through remix/ui
  • remix/response/compresscompressResponse. Use when compressing one-off responses outside compression() middleware
  • remix/response/file — file-download responses. Use for Content-Disposition: attachment responses
  • remix/route-pattern — low-level URL matching and generation. Use RoutePattern or createMatcher when working with raw patterns outside the router. href(...) encodes pathname and search params for you, and match(...) returns decoded params
  • remix/route-pattern/specificity — pattern ranking helpers. Use only when building custom matcher or reporting logic outside the normal router/matcher APIs
  • remix/fetch-proxy — Fetch-based HTTP proxying. Use to forward a request to another origin; pass xForwardedHeaders when the upstream needs forwarded proto, host, and port. It also rewrites proxied Set-Cookie domain/path attributes by default

Data, Validation, and Persistence

  • remix/data-schema — schema builders for runtime validation. Use for parse and parseSafe to validate any input that crosses a trust boundary, and .transform(...) when validated output should map to a different value or type
  • remix/data-schema/checks — common check helpers (email, minLength, maxLength, etc.). Use to compose into a schema
  • remix/data-schema/coerce — coercion helpers for strings, numbers, booleans, dates, and ids. Use when input arrives as a string but should be a typed value
  • remix/data-schema/form-dataf.object and f.field for parsing FormData directly. Use in actions that read browser forms
  • remix/data-schema/lazy — recursive or mutually-referential schemas. Use when a schema needs to refer to itself or another schema that is declared later
  • remix/data-table — typed tables and the shared Database API. Use table and column when modeling persisted data, then create a concrete database from the matching dialect package. Integration packages can implement DatabaseDriver and extend Database to add another SQL dialect
  • remix/data-table/sqlite, remix/data-table/postgres, remix/data-table/mysql — concrete database integrations. Use createSqliteDatabase, createPostgresDatabase, or createMysqlDatabase. SQLite accepts Node, Bun, and compatible synchronous clients with the shared prepare/exec surface
  • remix/data-table/migrations — migration authoring and registries. Use for createMigration and createMigrationRegistry; run migrations with Database.migrate()
  • remix/data-table/migrations/nodeloadMigrations from disk. Use in startup scripts that apply migrations
  • remix/data-table/operators — query operators such as inList(...). Use when where clauses need set or comparison logic
  • remix/data-table/sql-helpers — SQL helper utilities for database integrations or advanced query work. Avoid this in normal app code unless you are intentionally working below the table/query API

Auth, Sessions, and Cookies

  • remix/session — the Session object: get, set, flash, unset, regenerateId. Use for any per-browser state where tampering would be a bug (login, "I submitted this form already", cart, flash messages)
  • remix/middleware/sessionsession(cookie, storage). Use to wire a session cookie and storage backend into the middleware stack
  • remix/session-storage/fs, remix/session-storage/memory, remix/session-storage/cookie — storage backends. Use fs-storage for single-process apps, memory-storage for tests, cookie-storage for stateless deployments where data fits in a cookie
  • remix/session-storage/redis — Redis-backed storage. Use for multi-process or multi-host deployments
  • remix/session-storage/memcache — Memcache-backed storage. Same multi-host use case as Redis
  • remix/cookiecreateCookie for plain signed/unsigned cookies. Use for non-sensitive preferences where the client is allowed to control the value (theme, locale, dismissed banner). For state where tampering matters, prefer remix/session
  • remix/auth — credentials, OAuth, and OIDC providers. Use to define how identity is verified, start/finish external login, and refresh stored OAuth/OIDC token bundles with refreshExternalAuth(...)
  • remix/middleware/authauth({ schemes }), requireAuth, the Auth context key. Use to resolve identity into the request context and to gate routes

UI, Hydration, and Browser Behavior

  • remix/ui — the component runtime: components, core mixins, clientEntry, run, <Frame>, navigation helpers, and createRoot. Use for app UI behavior
  • remix/ui/server — low-level server rendering with renderToStream and renderToString. Normal apps should install render() from remix/middleware/render; use this subpath for custom pipelines and static string rendering
  • remix/ui-hmr — direct Remix UI component HMR transforms. Use only when writing a custom module hook or build integration
  • remix/ui-hmr/node — Node import hook for Remix UI component HMR. Use with --import remix/ui-hmr/node in development servers that run through remix/node-hmr
  • remix/ui-hmr/assetsremix/assets loader for Remix UI component HMR. Use uiHmr() in createAssetServer({ scripts: { loaders } }) during development
  • remix/ui/dev/refresh — development refresh support used by HMR tooling, not normal application code
  • remix/ui/animation — animation APIs: animateEntrance, animateExit, animateLayout, spring, tween, and easings
  • remix/ui/<primitive> — UI primitives, mixins, and component helpers. Current subpaths include remix/ui/accordion, remix/ui/anchor, remix/ui/button, remix/ui/checkbox, remix/ui/combobox, remix/ui/input, remix/ui/listbox, remix/ui/menu, remix/ui/popover, and remix/ui/select
  • remix/ui/test — component test rendering helpers such as render
  • remix/ui/jsx-runtime and remix/ui/jsx-dev-runtime — JSX transform targets. Configured in tsconfig.json, rarely imported directly
  • remix/html-template — escaped HTML template literals. Use when generating HTML outside the component system (RSS feeds, email bodies, error pages)
  • remix/file-storage — backend-agnostic File storage interface. Use as the type bound for upload destinations
  • remix/file-storage/fs, remix/file-storage/memory, remix/file-storage/s3 — storage backends. Use to implement an upload destination

Middleware

  • remix/middleware/renderrender({ assets?, onError? }) for the standard Remix UI renderer and renderWith(factory) for custom request-scoped renderers. Normal UI actions return context.render(node, init)
  • remix/middleware/staticstaticFiles(dir). Use to serve files from public/ exactly as they exist on disk
  • remix/middleware/form-dataformData(). Use to parse FormData once and expose it via get(FormData) instead of calling await request.formData() in each action
  • remix/form-data-parser — lower-level parseFormData, FileUpload. Use when implementing custom upload handlers. Upload handler errors propagate directly
  • remix/multipart-parser and remix/multipart-parser/node — low-level multipart stream parsing. MultipartPart.headers is a plain object keyed by lower-case header name; read values with bracket notation such as part.headers['content-type']
  • remix/middleware/compressioncompression(). Use for text-like responses
  • remix/middleware/loggerlogger(). Use in development for request logs; pass colors to force terminal color output on or off
  • remix/middleware/method-overridemethodOverride(). Use when HTML forms need PUT, PATCH, or DELETE
  • remix/middleware/async-contextasyncContext(), getContext(). Use when helpers outside actions need request context without threading it through every call
  • remix/middleware/corscors(opts?). Use for endpoints called cross-origin
  • remix/middleware/csrfcsrf(opts?). Use when session-backed forms mutate state and need synchronizer-token CSRF protection
  • remix/middleware/cop — cross-origin protection. Use to reject unsafe cross-origin browser requests

Test

  • remix/testdescribe, it, and lifecycle hooks. Use as the test framework
  • remix/test/cli — programmatic test runner APIs such as runRemixTest
  • remix/node-fetch-server/testcreateTestServer for end-to-end tests that need a real local HTTP server around a Fetch handler
  • remix/cli — programmatic Remix CLI API. Use the remix executable for project commands such as remix test, remix routes, remix doctor, and remix version
  • remix/assert — assertion helpers. Use in place of node:assert so messages render cleanly in the runner
  • remix/terminal — ANSI styles, color detection, style factories, and testable terminal streams. Use for CLIs and terminal output instead of hand-rolled escape sequences
  • remix/fs — small filesystem helpers such as openLazyFile and writeFile. Use in Node-only app or tooling code when you need lazy file responses or safe file writes
  • remix/lazy-fileLazyFile primitives and byte-range helpers. Use when implementing file or range responses below the higher-level response/file helpers
  • remix/mime — content-type and MIME detection helpers. Use instead of maintaining app-local extension maps
  • remix/tar-parser — streaming tar parsing. Use for import/export tooling that consumes tar archives

Canonical Patterns

Define routes first

import { form, get, post, resources, route } from 'remix/routes'

export const routes = route({
  home: '/',
  contact: form('contact'),
  books: {
    index: '/books',
    show: '/books/:slug',
  },
  auth: route('auth', {
    login: form('login'),
    logout: post('logout'),
  }),
  admin: route('admin', {
    index: get('/'),
    books: resources('books', { param: 'bookId' }),
  }),
})

Type controllers against the route contract

import { createController } from 'remix/router'

import { databaseContext } from '../middleware/database.ts'
import { routes } from '../routes.ts'

export default createController(routes.books, {
  actions: {
    async index({ get }) {
      let db = get(databaseContext)
      let allBooks = await db.findMany(books, { orderBy: ['id', 'asc'] })
      return render(<BooksIndexPage allBooks={allBooks} />)
    },
    async show({ get, params }) {
      let db = get(databaseContext)
      let book = await db.findOne(books, { where: { slug: params.slug } })
      if (!book) return new Response('Not Found', { status: 404 })
      return render(<BookShowPage book={book} />)
    },
  },
})

Register Controllers Explicitly

import { createRouter } from 'remix/router'

import rootController from './actions/controller.tsx'
import adminController from './actions/admin/controller.tsx'
import adminBooksController from './actions/admin/books/controller.tsx'
import authController from './actions/auth/controller.tsx'
import authLoginController from './actions/auth/login/controller.tsx'
import booksController from './actions/books/controller.tsx'
import contactController from './actions/contact/controller.tsx'
import { routes } from './routes.ts'

export const router = createRouter({ middleware })

router.map(routes, rootController)
router.map(routes.contact, contactController)
router.map(routes.books, booksController)
router.map(routes.auth, authController)
router.map(routes.auth.login, authLoginController)
router.map(routes.admin, adminController)
router.map(routes.admin.books, adminBooksController)

Compose middleware deliberately

import { createRouter } from 'remix/router'

let middleware = []

if (process.env.NODE_ENV === 'development') {
  middleware.push(logger())
}

middleware.push(compression())
middleware.push(staticFiles('./public'))
middleware.push(formData())
middleware.push(methodOverride())
middleware.push(session(cookie, storage))
middleware.push(asyncContext())
middleware.push(loadDatabase())
middleware.push(loadAuth())

let router = createRouter({ middleware })

Validate, mutate, and respond

import { createController } from 'remix/router'
import { redirect } from 'remix/response/redirect'
import * as s from 'remix/data-schema'
import * as f from 'remix/data-schema/form-data'
import { Session } from 'remix/session'

import { databaseContext } from '../middleware/database.ts'
import { routes } from '../routes.ts'

let bookSchema = f.object({
  slug: f.field(s.string()),
  title: f.field(s.string()),
})

export default createController(routes.books, {
  actions: {
    async create({ get }) {
      let parsed = s.parseSafe(bookSchema, get(FormData))
      if (!parsed.success) {
        return render(<NewBookPage errors={parsed.issues} />, { status: 400 })
      }

      let db = get(databaseContext)
      let book = await db.create(books, parsed.value)

      let session = get(Session)
      session.flash('message', `Added ${book.title}.`)

      return redirect(routes.books.show.href({ slug: book.slug }))
    },
  },
})

This shape works without JavaScript, returns a Response for every outcome, and is ready for clientEntry(...) interactivity when the UI needs it.

Build UI from handle props plus render

import { on, type Handle } from 'remix/ui'

function Counter(handle: Handle<{ initialCount?: number; label: string }>) {
  let count = handle.props.initialCount ?? 0

  return () => (
    <button
      mix={on('click', () => {
        count++
        handle.update()
      })}
    >
      {handle.props.label}: {count}
    </button>
  )
}

Only add clientEntry(...) and run(...) when the component needs browser interactivity or browser-only APIs.

Version History

  • 416a4e3 Current 2026-09-02 23:56

Same Skill Collection

.agents/skills/gap-analysis/SKILL.md
.agents/skills/i-have-adhd/SKILL.md
.agents/skills/pr-description/SKILL.md
.claude/skills/gap-analysis/SKILL.md
.claude/skills/i-have-adhd/SKILL.md
.claude/skills/pr-description/SKILL.md
.grok/skills/gap-analysis/SKILL.md
.grok/skills/i-have-adhd/SKILL.md
.grok/skills/pr-description/SKILL.md
packages/ai-code-mode/skills/ai-code-mode/SKILL.md
packages/ai-mcp/skills/ai-mcp/SKILL.md
packages/ai-memory/skills/tanstack-ai-memory-hindsight/SKILL.md
packages/ai-memory/skills/tanstack-ai-memory-honcho/SKILL.md
packages/ai-memory/skills/tanstack-ai-memory-in-memory/SKILL.md
packages/ai-memory/skills/tanstack-ai-memory-mem0/SKILL.md
packages/ai-memory/skills/tanstack-ai-memory-redis/SKILL.md
packages/ai-memory/skills/tanstack-ai-memory/SKILL.md
packages/ai-persistence/skills/ai-persistence/build-cloudflare-adapter/SKILL.md
packages/ai-persistence/skills/ai-persistence/build-cloudflare-artifact-store/SKILL.md
packages/ai-persistence/skills/ai-persistence/build-custom-adapter/SKILL.md
packages/ai-persistence/skills/ai-persistence/build-drizzle-adapter/SKILL.md
packages/ai-persistence/skills/ai-persistence/build-prisma-adapter/SKILL.md
packages/ai-persistence/skills/ai-persistence/server/SKILL.md
packages/ai-persistence/skills/ai-persistence/SKILL.md
packages/ai-persistence/skills/ai-persistence/stores/SKILL.md
packages/ai/skills/ai-core/ag-ui-protocol/SKILL.md
packages/ai/skills/ai-core/chat-experience/SKILL.md
packages/ai/skills/ai-core/custom-backend-integration/SKILL.md
packages/ai/skills/ai-core/debug-logging/SKILL.md
packages/ai/skills/ai-core/locks/SKILL.md
packages/ai/skills/ai-core/middleware/SKILL.md
packages/ai/skills/ai-core/SKILL.md
packages/ai/skills/ai-core/tool-calling/SKILL.md
testing/panel/skills/emoji-storyteller/SKILL.md
testing/panel/skills/haiku/SKILL.md
testing/panel/skills/pirate-speak/SKILL.md
.agents/skills/bugfix-pr/SKILL.md
.agents/skills/docs/SKILL.md
.agents/skills/ponytail/SKILL.md
.agents/skills/pr-sweep/SKILL.md
.agents/skills/simple-english/SKILL.md
.agents/skills/triage-github/SKILL.md
.claude/skills/bugfix-pr/SKILL.md
.claude/skills/docs/SKILL.md
.claude/skills/ponytail/SKILL.md
.claude/skills/pr-sweep/SKILL.md
.claude/skills/simple-english/SKILL.md
.claude/skills/triage-github/SKILL.md
.grok/skills/bugfix-pr/SKILL.md

Metadata

Files
0
Version
416a4e3
Hash
db9c941a
Indexed
2026-09-02 23:56

inicio - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-09-03 09:57
浙ICP备14020137号-1 $mapa de visitantes$