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.
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