| name | layout-shells |
| description | Layout shells in an AdonisJS + Inertia app. Instead of a runtime layout chooser, each context has its own purpose-built shell that coexists as a distinct React component. Every page picks its shell explicitly by import. Use when adding a new page (which shell to import), when adding a new area of the app (which shell fits), or when someone proposes runtime configurability. Trigger on: "which layout", "shell", "sidebar", "top nav", "layout choice". |
| license | MIT |
| allowed-tools | Read, Edit, Write, Bash, Grep, Glob |
Layout shells
Every page imports the shell it needs; there is no runtime chooser. Variant / collapsible / direction / max-width are hardcoded inside the shell file — not props, not user-facing settings. If a project spun from this template doesn't need one of the shells (e.g. no marketing area), the shell file is deleted, not "disabled by config". The philosophy is starting clean over configuring at runtime — configurability adds branches that need to be tested, translated, permissioned; deletion doesn't.
Rules
Shell inventory (typical set)
Every app spun from this template starts with two:
AppLayout — the logged-in shell. Sidebar on desktop, bottom nav + top notification bar on mobile. Wraps every internal page (dashboard, users, settings, feature modules).
AuthLayout — the pre-login shell (sign-in, sign-up, password flows). Simpler, no nav.
Public marketing pages typically do not use a shell — they compose their own sections (<Hero>, <Features>, <Footer>) directly, because their chrome has nothing in common with the app.
Any additional shell (checkout, kiosk, print-friendly) is added only when its chrome is truly distinct.
Repo refs
- The four shells:
app/common/ui/components/admin_layout.tsx, app/common/ui/components/authenticated_layout.tsx, app/auth/ui/components/auth_layout.tsx, app/marketing/ui/components/marketing_layout.tsx.
- Nav config (single file):
app/common/ui/config/navigation.config.ts.
Doc refs
Workflow
Pick the shell for a new page
- Login / sign-up / password reset →
AuthLayout.
- Any other logged-in page →
AppLayout.
- Public marketing page (landing, about, blog) → no shell; compose sections directly.
- Any other context (checkout, installer, kiosk) → add a new shell only if its chrome shares nothing with the existing ones.
Add a nav item
Edit the navigation config file — getMainNav(t) for the primary nav, getFooterNav(t) for footer/utility items, getNavUser(t) for the user dropdown (shared across logged-in shells). Set can: 'permissionKey' to gate visibility. Do not put the item inside the shell file.
Delete an unused shell in a downstream project
For a project that doesn't need one of the shells:
- Delete the shell file.
- Delete any pages that used it.
- Delete the nav config section that fed it (a section that only its shell reads).
Nothing else references the shells — no runtime config, no feature flag.
Add a new shell (rare)
Only when the shape is genuinely new. Copy the closest existing shell, hardcode the new decisions (variant / direction / max-width), keep the same widget row in the header (bell + theme + lang + user) if the shell is authenticated.
Anti-patterns
- ❌ Adding
variant / collapsible / direction as props on a shell — hardcode them; if the project needs a different shape, add a different shell.
- ❌ Wrapping a shell in a context provider to switch behavior at runtime.
- ❌ Configuring shells via user preferences (theme + language are user prefs; layout choice is not).
- ❌ Nesting one shell inside another.
- ❌ A wrapper component that picks a shell based on the route or role — the page picks. Explicit beats magic.
- ❌ Hardcoding nav items inside the shell file — always go through the nav config functions.
- ❌ Adding page-only providers inside a shell — providers belong at the root client entry so every page and every shell sees them.
Related skills
[[inertia]] · [[authorization]] · [[i18n]] · [[notifications]]