skills/remix-project-layout/SKILL.md
Describe the ideal layout of a Remix application, including canonical directories, route ownership, naming conventions, and file locations on disk. When asked to bootstrap that layout in a new directory, run the bundled TypeScript script.
npx skillsauth add remix-run/remix remix-project-layoutInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use this skill when defining, reviewing, or bootstrapping the on-disk layout of a Remix application.
This skill is about structure and conventions. It defines where code belongs, how route ownership maps to files on disk, and how a Remix app should be organized as it grows. When the user wants a new app scaffolded, run the bundled script instead of recreating the starter files by hand.
Use these root directories consistently:
app/ for runtime application codedb/ for database artifacts such as migrations and SQLite filespublic/ for static files served as-istest/ for shared test helpers, fixtures, and cross-app integration coveragetmp/ for runtime scratch files such as uploads, caches, or local session filesInside app/, organize code by responsibility:
assets/ for client entrypoints and client-owned behaviorcontrollers/ for route-owned handlers and route-local UIdata/ for schema, queries, persistence setup, and runtime data initializationmiddleware/ for request lifecycle concerns such as auth, sessions, database injection, and
uploadsui/ for shared cross-route UI primitivesutils/ for genuinely cross-layer runtime helpersroutes.ts for the route contractrouter.ts for router setup and route wiringWhen code could plausibly live in more than one place, use this order of precedence:
app/ui/.app/middleware/.app/data/.app/utils/ only when the code is genuinely cross-layer and does not clearly belong to one
of the other app layers.Prefer moving code to a narrower owner over introducing generic shared buckets.
The disk layout should make it possible to start from a route key in app/routes.ts and find the
implementation immediately.
Use a flat file in app/controllers/ when a route is implemented by one exported BuildAction.
Examples:
routes.home -> app/controllers/home.tsxroutes.about -> app/controllers/about.tsxroutes.search -> app/controllers/search.tsxroutes.uploads -> app/controllers/uploads.tsxFlat leaf route files are self-contained. If a helper component is used only by that route, keep it in the same module.
Use a folder with controller.tsx when the route is implemented by a Controller, owns nested
child routes, or owns multiple actions such as index, action, show, or update.
Examples:
routes.contact -> app/controllers/contact/controller.tsxroutes.auth -> app/controllers/auth/controller.tsxroutes.account -> app/controllers/account/controller.tsxroutes.cart -> app/controllers/cart/controller.tsxIf a route is nested in app/routes.ts, mirror that nesting on disk.
Examples:
routes.auth.login -> app/controllers/auth/login/controller.tsxroutes.account.settings -> app/controllers/account/settings/controller.tsxroutes.account.orders -> app/controllers/account/orders/controller.tsxroutes.cart.api -> app/controllers/cart/api/controller.tsxroutes.admin.users -> app/controllers/admin/users/controller.tsxIf UI is reused across route areas, it belongs in app/ui/, not under app/controllers/.
Examples:
app/ui/document.tsxapp/ui/layout.tsxapp/ui/form-field.tsxapp/ui/restful-form.tsxOnly keep a component inside a controller folder when that UI is owned by that route or controller feature.
If a page component or helper is used only by one action or controller feature, keep it next to the owning route.
Examples:
app/controllers/contact/page.tsxapp/controllers/auth/login/page.tsxapp/controllers/admin/books/form.tsxapp/controllers/books/index-page.tsxFor flat leaf actions, route-local UI lives in the same file as the action.
If a route starts as a flat leaf file and later grows child routes or multiple actions, promote it from:
app/controllers/home.tsxto:
app/controllers/home/controller.tsxDo this only when the route actually needs it. Do not create controller folders preemptively for every leaf route.
Use predictable file names so route ownership is obvious without opening files:
controller.tsx for controller entrypointspage.tsx for a single controller-owned page moduleindex-page.tsx, show-page.tsx, edit-page.tsx, and form.tsx for resource-style controller
UIhome.tsx, about.tsx, search.tsx, or
uploads.tsxhome.test.ts or controller.test.tsDo not invent one-off naming schemes when an existing convention already fits.
When the user wants this layout scaffolded into a new directory, run:
node skills/remix-project-layout/scripts/bootstrap_remix_application.ts <target-dir>
Optional flags:
--app-name <name> to override the generated app name--remix-version <version> to override the default remix version--force to write into a non-empty target directoryThe script generates the starter app, including README.md, route handlers, shared UI, test
helpers, and the root directory structure described in this skill.
app/lib/ as a generic dumping ground.app/components/ as a second shared UI bucket when app/ui/ already owns that
role.app/controllers/.app/ui/.app/utils/ when they
belong in app/middleware/.app/utils/ when it belongs in app/data/.helpers.ts, common.ts, or misc.ts unless the name is truly
accurate for the module's ownership.tools
Build the UI of a Remix app. Use when creating pages, layouts, client entries, interactions, stateful UI, navigation, hydration, styling, animations, reusable mixins, or UI tests.
documentation
Write or rewrite package README files in the style used by the Remix repository. Use when drafting a new package README, revising an existing README, or reviewing README structure, examples, installation instructions, and section ordering for Remix packages.
development
Write or audit public API docs for Remix packages. Use when adding or tightening JSDoc on exported functions, classes, interfaces, type aliases, or option objects.
documentation
Update an existing GitHub pull request title and description so they accurately describe the pull request as it exists now. Use when the user asks to update, rewrite, refresh, fix, or tighten a PR title/body, or when the PR scope has changed and the metadata needs to be brought back in sync.