Loading...
Loading...
Server-driven architecture patterns for Inertia Rails + React. Load this FIRST when building any Inertia page or feature — it routes to the right skill. Decision matrix for data loading, forms, navigation, state management. NEVER useEffect+fetch, NEVER redirect_to for external URLs (use inertia_location), NEVER react-hook-form (use Form component). MUST invoke when adding pages, models with views, CRUD, or displaying data in an Inertia Rails app. ALWAYS `render inertia: { key: value }` to pass data — `@ivars` are NOT auto-passed as props.
npx skill4agent add inertia-rails/skills inertia-rails-architectureuseState| Need | Solution | NOT This |
|---|---|---|
| Page data from server | Controller props | useEffect + fetch |
| Global data (auth, config) | | React Context / Redux |
| Flash messages / toasts | Rails | inertia_share / React state |
| Form submission | | fetch/axios + useState |
| Navigate between pages | | react-router / window.location |
| Refresh specific data | | React Query / SWR |
| Expensive server data | | useEffect + loading state |
| Infinite scroll | | Client-side pagination |
| Stable reference data | | Cache in React state |
| Real-time updates (core) | ActionCable + | Polling with setInterval |
| Simple polling (MVP/prototyping) | | setInterval + router.reload |
| URL-driven UI state (dialogs, tabs) | Controller reads | useEffect + window.location |
| Ephemeral UI state | | Server props |
| External API calls | Dedicated API endpoint | Mixing with Inertia props |
| # | Impact | Rule | WHY |
|---|---|---|---|
| 1 | CRITICAL | Never useEffect+fetch for page data | Inertia re-renders the full component on navigation; a useEffect fetch creates a second data lifecycle that drifts from props and causes stale UI |
| 2 | CRITICAL | Never check auth client-side | Auth state in React can be spoofed; server-side checks are the only real gate. Client-side "guards" give false security |
| 3 | CRITICAL | Use | |
| 4 | HIGH | Use | |
| 5 | HIGH | Use partial reloads, not React Query/SWR | React Query adds a second cache layer that conflicts with Inertia's page-based caching and versioning |
| 5b | HIGH | Use | |
| 6 | HIGH | Use | Context re-renders consumers on every change; shared props are per-request and integrated with partial reloads |
| 7 | HIGH | Use Rails flash for notifications, not shared props | Flash auto-clears after one response; shared props persist until explicitly changed, causing stale toasts |
| 8 | MEDIUM | Use deferred/optional props for expensive queries | Blocks initial render otherwise — user sees blank page until slow query finishes |
| 9 | MEDIUM | Use persistent layouts for state preservation | Without persistent layout, layout remounts on every navigation — scroll position, audio playback, and component state are lost |
| 10 | MEDIUM | Keep React components as renderers, not data fetchers | Mixing data-fetching into components makes them untestable and breaks Inertia's server-driven model |
| Workflow | Load these skills |
|---|---|
| New page with props | |
| Form with validation | |
| shadcn form inputs | |
| Flash toasts | |
| Deferred/lazy data | |
| URL-driven dialog/tabs | |
| Alba serialization | |
| Testing controllers | |
references/AGENTS.mdreferences/decision-trees.md| Signal | Why | Example |
|---|---|---|
| Non-browser consumer | Inertia's JSON envelope (component, props, url, version) is designed for the frontend adapter — other consumers can't use it | Mobile API, CLI tools, payment webhooks |
| Large-dataset search | Dataset is too big to load as a prop; each input needs per-keystroke server filtering. Use raw fetch for the search, let Inertia handle post-selection side effects via props. | City/address autocomplete, postal code lookup |
| Binary/streaming response | Inertia can only deliver JSON props. Use a separate route with a standard download response. | PDF/CSV export, file downloads |