inertia
Installation
SKILL.md
Inertia
Inertia is the bridge between AdonisJS routes and React pages: controllers call inertia.render('<name>', { props }), the client resolves that name to a .tsx file inside a module's ui/pages/. Shared props flow globally through an Inertia middleware. Forms use @inertiajs/react's useForm; URLs come from Tuyau's urlFor('route.name', params?) for type safety. Modals are Inertia responses too — a controller helper renders one page over another. Every provider (theme, i18n, tooltip, Tuyau, modal stack) is wired at the root client entry.
Rules
- Page location:
app/<mod>/ui/pages/<subpath>/<page>.tsx. Controllers reference the page by the path minus.tsxand everything before<mod>/:inertia.render('users/index')→app/users/ui/pages/index.tsx,inertia.render('users/create')→app/users/ui/pages/create.tsx. First segment = module. - Shell import: every page imports its layout shell explicitly ([[layout-shells]]). No runtime chooser.
- Client entry mounts the global provider tree — theme, i18n, tooltip, Tuyau, modal stack. Don't reorder without reason; theme + i18n need to be outermost so shells see them.
- Shared props: added by the Inertia middleware via
ctx.inertia.always(...). Available on the frontend via a typedusePageProps<{...}>()hook. Per-request data (page-specific) goes in the controller'sinertia.render(...)call, not in the middleware. - Form: default is
useForm(initial)from@inertiajs/react+ a plain<form onSubmit>. Callpost(urlFor('route.name'), { onSuccess, onFinish, preserveScroll }). A local<Form>wrapper is provided that adds aFormErrorsContextso nested fields can pull errors from context — use it when the form is deep enough that field components want the errors without prop-drilling. - URLs: never hardcode. Use
urlFor('route.name', { param: value })from Tuyau. Route names come fromrouter.get(...).as('route.name')in the module'sroutes.ts. - Modals: controllers return the modal helper (
modal(inertia, 'page', props, { route: 'backdrop.route' })). The modal page wraps content in the modal component and receives acloserender prop. - Navigation between pages:
router.visit(url);router.reload({ only: ['propName'], async: true })for partial reloads;router.get/post/put/delete(url, data, options)for programmatic form submits. - Types: server-generated data types come from an ace codegen; the client alias makes them available across pages. Don't duplicate the type in the page —
import typefrom the codegen path.