laravel-inertia-react-structure
Laravel Inertia React Frontend Structure
Based on Spatie's conventions for structuring production Laravel Inertia React applications.
Directory Structure
Four base directories under resources/js:
resources/js/
├── common/ # Generic, reusable code portable across projects
├── modules/ # Project-specific code shared across multiple pages
├── pages/ # Inertia page components
└── shadcn/ # Auto-generated shadcn/ui components (if used)
common vs modules: ask "Does it relate to a domain or feature?" If yes → modules. If it's generic and project-agnostic → common.
Naming Conventions
- Components and React contexts:
PascalCase(e.g.Button.tsx,AuthContext.tsx) - Other files (helpers, hooks, constants, stores):
camelCase(e.g.useAuth.ts,formatDate.ts) - Directories:
kebab-case(e.g.date-picker/,user-management/)
Module Organization
Small modules have a few top-level files. Larger modules organize by type:
modules/agenda/
├── components/
├── contexts/
├── constants/
├── helpers/
├── hooks/
├── stores/
└── types.ts # or types/ directory if large
The common/ directory follows the same structure.
Pages Directory
Pages mirror the URL structure. Components are suffixed with Page.
pages/
├── layouts/ # Global layouts
├── admin/
│ ├── layouts/ # Section-specific layouts
│ ├── users/
│ │ ├── components/ # Page-specific partials
│ │ ├── helpers/
│ │ ├── IndexPage.tsx
│ │ └── EditPage.tsx
│ └── DashboardPage.tsx
└── auth/
├── LoginPage.tsx
└── RegisterPage.tsx
React Component Conventions
Use function declarations (not const arrow functions) and named exports exclusively:
// Correct
export function Button({ variant, className }: ButtonProps) {
return <button className={cn(variant, className)}>Click</button>;
}
// Wrong: const + default export
const Button = ({ variant }) => { ... };
export default Button;
One component per file. No barrel files (index.ts re-exports).
Import Organization
Two blocks separated by a blank line: library imports first, then application imports. Use absolute paths with aliases (@/):
import { useState } from "react";
import { cn } from "@/common/helpers/cn";
import { useAgenda } from "@/modules/agenda/hooks/useAgenda";
import { AgendaItem } from "@/modules/agenda/components/AgendaItem";
Props
Sort alphabetically, with className and children last:
interface DialogProps {
onClose: () => void;
open: boolean;
title: string;
className?: string;
children: React.ReactNode;
}
Stylesheets
Use Tailwind. Single app.css for most projects. Larger projects split into:
resources/css/
├── base/
├── components/
└── utilities/
Multi-Zone Applications
For apps with distinct sections (e.g. admin/client), introduce apps/:
resources/js/
├── common/ # Shared across all zones
├── modules/ # Global modules shared across zones
├── apps/
│ ├── admin/
│ │ ├── modules/ # Admin-specific modules
│ │ ├── pages/
│ │ └── app.tsx
│ └── client/
│ ├── modules/ # Client-specific modules
│ ├── pages/
│ └── app.tsx
└── shadcn/
shadcn/ui Usage
Abstract shadcn components into simpler, project-specific implementations rather than using the low-level API directly in application code. Place abstractions in common/ or modules/ as appropriate.