Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Laravel React Starter Kit
https://github.com/laravel/react-starter-kit
Admin
The Laravel + React Starter Kit provides a robust, modern starting point for building Laravel
...
Tokens:
11,271
Snippets:
28
Trust Score:
9.5
Update:
4 months ago
Context
Skills
Chat
Benchmark
52.1
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Laravel + React Starter Kit This is a modern full-stack web application starter kit that combines Laravel's powerful backend framework with React's component-based frontend architecture. Built on Inertia.js, it provides seamless client-side routing with server-side controllers, eliminating the need for a separate API layer. The starter kit delivers a complete authentication system with two-factor authentication, user profile management, email verification, and password reset functionality out of the box. The application leverages React 19 with TypeScript for type safety, Tailwind CSS 4 for utility-first styling, and the shadcn/ui component library built on Radix UI primitives. Laravel Fortify handles authentication concerns on the backend, while Laravel Wayfinder generates type-safe route helpers for the frontend. The project uses Vite 7 for lightning-fast hot module replacement during development and optimized production builds, along with the React Compiler for automatic performance optimization. This architecture enables developers to build modern single-page applications while maintaining the simplicity and productivity of traditional server-side rendering. ## Authentication APIs ### User Registration Endpoint Register a new user account with name, email, and password. Laravel Fortify handles the registration logic using a custom action class that validates input, creates the user record with hashed password, triggers the Registered event, and automatically logs in the new user. ```php // Backend: app/Actions/Fortify/CreateNewUser.php <?php namespace App\Actions\Fortify; use App\Models\User; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Laravel\Fortify\Contracts\CreatesNewUsers; class CreateNewUser implements CreatesNewUsers { use PasswordValidationRules; public function create(array $input): User { Validator::make($input, [ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 'password' => $this->passwordRules(), ])->validate(); return User::create([ 'name' => $input['name'], 'email' => $input['email'], 'password' => Hash::make($input['password']), ]); } } // Fortify configuration: app/Providers/FortifyServiceProvider.php Fortify::createUsersUsing(CreateNewUser::class); Fortify::registerView(fn () => Inertia::render('auth/register')); // Frontend: resources/js/pages/auth/register.tsx import { register as registerAction } from '@/routes/register'; import { Form } from '@inertiajs/react'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; export default function Register() { return ( <Form {...registerAction.form()} resetOnSuccess={['password', 'password_confirmation']} className="space-y-4" > {({ processing, errors }) => ( <> <Input name="name" type="text" required autoComplete="name" /> <InputError message={errors.name} /> <Input name="email" type="email" required autoComplete="email" /> <InputError message={errors.email} /> <Input name="password" type="password" required autoComplete="new-password" /> <InputError message={errors.password} /> <Input name="password_confirmation" type="password" required autoComplete="new-password" /> <InputError message={errors.password_confirmation} /> <Button type="submit" disabled={processing}>Create account</Button> </> )} </Form> ); } ``` ### User Login Endpoint Authenticate a user with email and password, with support for remember-me functionality and two-factor authentication. Laravel Fortify handles the login logic automatically, including 2FA checks. If 2FA is enabled for the user, Fortify redirects to the two-factor challenge page instead of logging in directly. ```php // Backend: Laravel Fortify handles login automatically // Route: POST /login // Configured in: app/Providers/FortifyServiceProvider.php Fortify::loginView(fn (Request $request) => Inertia::render('auth/login', [ 'canResetPassword' => Features::enabled(Features::resetPasswords()), 'canRegister' => Features::enabled(Features::registration()), 'status' => $request->session()->get('status'), ])); // Logout route: POST /logout (handled by Fortify) // Frontend: resources/js/pages/auth/login.tsx import { store } from '@/routes/login'; import { request } from '@/routes/password'; import { Form } from '@inertiajs/react'; import { Checkbox } from '@/components/ui/checkbox'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; export default function Login({ canResetPassword, canRegister }: { canResetPassword: boolean; canRegister: boolean; }) { return ( <Form {...store.form()} resetOnSuccess={['password']} > {({ processing, errors }) => ( <> <Input name="email" type="email" required autoComplete="email" /> <InputError message={errors.email} /> <Input name="password" type="password" required autoComplete="current-password" /> <InputError message={errors.password} /> <Checkbox name="remember" id="remember" /> <Label htmlFor="remember">Remember me</Label> {canResetPassword && ( <TextLink href={request()}>Forgot password?</TextLink> )} <Button type="submit" disabled={processing}>Log in</Button> </> )} </Form> ); } ``` ### Password Reset Request Send a password reset link to the user's email address. Laravel Fortify validates the email exists in the system and uses Laravel's password broker to send the reset link with a signed URL token. ```php // Backend: Laravel Fortify handles password reset requests // Routes automatically registered: // GET /forgot-password - Show password reset request form // POST /forgot-password - Send reset link email // Configured in: app/Providers/FortifyServiceProvider.php Fortify::requestPasswordResetLinkView(fn (Request $request) => Inertia::render('auth/forgot-password', [ 'status' => $request->session()->get('status'), ]) ); // Frontend: resources/js/pages/auth/forgot-password.tsx import { email } from '@/routes/password'; import { Form } from '@inertiajs/react'; export default function ForgotPassword({ status }: { status?: string }) { return ( <Form {...email.form()}> {({ processing, errors }) => ( <> <Input name="email" type="email" required autoComplete="email" placeholder="email@example.com" /> <InputError message={errors.email} /> <Button type="submit" disabled={processing}> Email password reset link </Button> </> )} </Form> ); } // Usage: POST to /forgot-password curl -X POST http://localhost/forgot-password \ -H "Content-Type: application/json" \ -d '{"email": "user@example.com"}' ``` ### Email Verification Verify user email addresses via signed URL links. Laravel Fortify handles email verification automatically. Users receive an email with a verification link after registration, and certain routes require verified emails using the 'verified' middleware. ```php // Backend: Laravel Fortify registers these routes automatically // GET /verify-email - Show verification notice page // GET /verify-email/{id}/{hash} - Verify email via signed URL // POST /email/verification-notification - Resend verification email // Configured in: app/Providers/FortifyServiceProvider.php Fortify::verifyEmailView(fn (Request $request) => Inertia::render('auth/verify-email', [ 'status' => $request->session()->get('status'), ]) ); // config/fortify.php - Enable feature 'features' => [ Features::emailVerification(), ], // Protected route requiring verification: routes/web.php Route::middleware(['auth', 'verified'])->group(function () { Route::get('dashboard', fn() => Inertia::render('dashboard'))->name('dashboard'); }); // Frontend: resources/js/pages/auth/verify-email.tsx import { send } from '@/routes/verification'; import { Link } from '@inertiajs/react'; export default function VerifyEmail({ status }: { status?: string }) { return ( <> <p> Thanks for signing up! Before getting started, please verify your email address by clicking the link we emailed to you. </p> <Link href={send()} as="button" method="post"> Resend verification email </Link> {status === 'verification-link-sent' && ( <p>A new verification link has been sent!</p> )} </> ); } ``` ## Two-Factor Authentication ### Enable Two-Factor Authentication Generate QR code and secret key for setting up two-factor authentication using authenticator apps like Google Authenticator or Authy. Laravel Fortify provides built-in endpoints for 2FA management. ```typescript // Frontend: resources/js/hooks/use-two-factor-auth.ts import { qrCode, recoveryCodes, secretKey } from '@/routes/two-factor'; import { useCallback, useMemo, useState } from 'react'; export const useTwoFactorAuth = () => { const [qrCodeSvg, setQrCodeSvg] = useState<string | null>(null); const [manualSetupKey, setManualSetupKey] = useState<string | null>(null); const [recoveryCodesList, setRecoveryCodesList] = useState<string[]>([]); const [errors, setErrors] = useState<string[]>([]); const hasSetupData = useMemo<boolean>( () => qrCodeSvg !== null && manualSetupKey !== null, [qrCodeSvg, manualSetupKey] ); const fetchQrCode = useCallback(async (): Promise<void> => { try { const { svg } = await fetch(qrCode.url(), { headers: { Accept: 'application/json' } }).then(r => r.json()); setQrCodeSvg(svg); } catch { setErrors((prev) => [...prev, 'Failed to fetch QR code']); } }, []); const fetchSetupKey = useCallback(async (): Promise<void> => { try { const { secretKey: key } = await fetch(secretKey.url(), { headers: { Accept: 'application/json' } }).then(r => r.json()); setManualSetupKey(key); } catch { setErrors((prev) => [...prev, 'Failed to fetch setup key']); } }, []); const fetchSetupData = useCallback(async (): Promise<void> => { await Promise.all([fetchQrCode(), fetchSetupKey()]); }, [fetchQrCode, fetchSetupKey]); const fetchRecoveryCodes = useCallback(async (): Promise<void> => { try { const codes = await fetch(recoveryCodes.url(), { headers: { Accept: 'application/json' } }).then(r => r.json()); setRecoveryCodesList(codes); } catch { setErrors((prev) => [...prev, 'Failed to fetch recovery codes']); } }, []); return { qrCodeSvg, manualSetupKey, recoveryCodesList, hasSetupData, errors, fetchSetupData, fetchRecoveryCodes }; }; // Usage in component const { qrCodeSvg, manualSetupKey, fetchSetupData, fetchRecoveryCodes } = useTwoFactorAuth(); // Enable 2FA: POST to /user/two-factor-authentication await fetch('/user/two-factor-authentication', { method: 'POST' }); await fetchSetupData(); // Get QR code and setup key // Confirm with OTP code: POST to /user/confirmed-two-factor-authentication await fetch('/user/confirmed-two-factor-authentication', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ code: '123456' }) }); await fetchRecoveryCodes(); // Get recovery codes after confirmation ``` ### Two-Factor Challenge Login Verify two-factor authentication code during login process when 2FA is enabled for the user account. ```php // Backend: Laravel Fortify handles 2FA routes automatically // Route: POST /two-factor-challenge // Body: { "code": "123456" } or { "recovery_code": "abcd-efgh" } // Frontend usage with Inertia form import { Form } from '@inertiajs/react'; import { InputOTP } from '@/components/ui/input-otp'; export default function TwoFactorChallenge() { const [useRecoveryCode, setUseRecoveryCode] = useState(false); return ( <Form action="/two-factor-challenge" method="post"> {({ processing, errors }) => ( <> {!useRecoveryCode ? ( <> <InputOTP maxLength={6} name="code" required /> <InputError message={errors.code} /> </> ) : ( <> <Input name="recovery_code" required placeholder="abcd-efgh" /> <InputError message={errors.recovery_code} /> </> )} <Button type="submit" disabled={processing}>Verify</Button> <Button type="button" variant="link" onClick={() => setUseRecoveryCode(!useRecoveryCode)} > {useRecoveryCode ? 'Use authenticator code' : 'Use recovery code'} </Button> </> )} </Form> ); } ``` ## User Profile Management ### Update User Profile Update authenticated user's name and email address. If email changes, verification status is reset requiring re-verification. Uses custom controller instead of Fortify for profile updates. ```php // Backend: app/Http/Controllers/Settings/ProfileController.php <?php namespace App\Http\Controllers\Settings; use App\Http\Controllers\Controller; use App\Http\Requests\Settings\ProfileUpdateRequest; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Inertia\Inertia; use Inertia\Response; class ProfileController extends Controller { public function edit(Request $request): Response { return Inertia::render('settings/profile', [ 'mustVerifyEmail' => $request->user() instanceof MustVerifyEmail, 'status' => $request->session()->get('status'), ]); } public function update(ProfileUpdateRequest $request): RedirectResponse { $request->user()->fill($request->validated()); if ($request->user()->isDirty('email')) { $request->user()->email_verified_at = null; } $request->user()->save(); return to_route('profile.edit'); } } // Request validation: app/Http/Requests/Settings/ProfileUpdateRequest.php public function rules() { return [ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', Rule::unique(User::class)->ignore($this->user()->id)], ]; } // Routes: routes/settings.php Route::get('settings/profile', [ProfileController::class, 'edit'])->name('profile.edit'); Route::patch('settings/profile', [ProfileController::class, 'update'])->name('profile.update'); // Frontend: resources/js/pages/settings/profile.tsx import ProfileController from '@/actions/App/Http/Controllers/Settings/ProfileController'; import { usePage } from '@inertiajs/react'; export default function Profile() { const { auth } = usePage<SharedData>().props; return ( <Form {...ProfileController.update.form()}> {({ processing, errors }) => ( <> <Input name="name" defaultValue={auth.user.name} required /> <Input name="email" type="email" defaultValue={auth.user.email} required /> <Button type="submit" disabled={processing}>Save changes</Button> </> )} </Form> ); } ``` ### Update Password Change the authenticated user's password with current password confirmation and new password validation. Uses a custom controller with rate limiting for security. ```php // Backend: app/Http/Controllers/Settings/PasswordController.php <?php namespace App\Http\Controllers\Settings; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use Illuminate\Validation\Rules\Password; class PasswordController extends Controller { public function edit() { return Inertia::render('settings/password'); } public function update(Request $request) { $validated = $request->validate([ 'current_password' => ['required', 'current_password'], 'password' => ['required', Password::defaults(), 'confirmed'], ]); $request->user()->update([ 'password' => Hash::make($validated['password']), ]); return back(); } } // Routes: routes/settings.php Route::middleware('auth')->group(function () { Route::get('settings/password', [PasswordController::class, 'edit']) ->name('user-password.edit'); Route::put('settings/password', [PasswordController::class, 'update']) ->middleware('throttle:6,1') ->name('user-password.update'); }); // Frontend: Use Wayfinder-generated form helpers import PasswordController from '@/actions/App/Http/Controllers/Settings/PasswordController'; <Form {...PasswordController.update.form()}> {({ processing, errors }) => ( <> <Input type="password" name="current_password" required /> <Input type="password" name="password" required /> <Input type="password" name="password_confirmation" required /> <Button type="submit" disabled={processing}>Update password</Button> </> )} </Form> ``` ### Delete User Account Permanently delete the authenticated user's account after verifying their password. The user is logged out and redirected to the home page. ```php // Backend: app/Http/Controllers/Settings/ProfileController.php public function destroy(Request $request): RedirectResponse { $request->validate([ 'password' => ['required', 'current_password'], ]); $user = $request->user(); Auth::logout(); $user->delete(); $request->session()->invalidate(); $request->session()->regenerateToken(); return redirect('/'); } // Route: routes/settings.php Route::middleware('auth')->group(function () { Route::delete('settings/profile', [ProfileController::class, 'destroy']) ->name('profile.destroy'); }); // Frontend: resources/js/components/delete-user.tsx import ProfileController from '@/actions/App/Http/Controllers/Settings/ProfileController'; import { Form } from '@inertiajs/react'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; export default function DeleteUser() { const [open, setOpen] = useState(false); return ( <Dialog open={open} onOpenChange={setOpen}> <DialogTrigger asChild> <Button variant="destructive">Delete Account</Button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Are you absolutely sure?</DialogTitle> <DialogDescription> This action cannot be undone. Enter your password to confirm deletion. </DialogDescription> </DialogHeader> <Form {...ProfileController.destroy.form()}> {({ processing, errors }) => ( <> <Input type="password" name="password" placeholder="Enter your password" required /> <InputError message={errors.password} /> <Button type="submit" variant="destructive" disabled={processing}> Delete Account </Button> </> )} </Form> </DialogContent> </Dialog> ); } ``` ## Frontend Hooks and Utilities ### Theme/Appearance Management Manage light, dark, and system theme preferences with localStorage persistence and cookie-based SSR support. Automatically responds to system theme changes when in system mode. ```typescript // resources/js/hooks/use-appearance.tsx import { useCallback, useEffect, useState } from 'react'; export type Appearance = 'light' | 'dark' | 'system'; const prefersDark = () => { if (typeof window === 'undefined') return false; return window.matchMedia('(prefers-color-scheme: dark)').matches; }; const setCookie = (name: string, value: string, days = 365) => { if (typeof document === 'undefined') return; const maxAge = days * 24 * 60 * 60; document.cookie = `${name}=${value};path=/;max-age=${maxAge};SameSite=Lax`; }; const applyTheme = (appearance: Appearance) => { const isDark = appearance === 'dark' || (appearance === 'system' && prefersDark()); document.documentElement.classList.toggle('dark', isDark); document.documentElement.style.colorScheme = isDark ? 'dark' : 'light'; }; export function useAppearance() { const [appearance, setAppearance] = useState<Appearance>('system'); const updateAppearance = useCallback((mode: Appearance) => { setAppearance(mode); localStorage.setItem('appearance', mode); setCookie('appearance', mode); applyTheme(mode); }, []); useEffect(() => { const savedAppearance = localStorage.getItem('appearance') as Appearance | null; updateAppearance(savedAppearance || 'system'); // Listen for system theme changes const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); const handleChange = () => { const current = localStorage.getItem('appearance') as Appearance; applyTheme(current || 'system'); }; mediaQuery.addEventListener('change', handleChange); return () => mediaQuery.removeEventListener('change', handleChange); }, [updateAppearance]); return { appearance, updateAppearance } as const; } // Usage in component: resources/js/pages/settings/appearance.tsx import { useAppearance } from '@/hooks/use-appearance'; import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'; function AppearanceSettings() { const { appearance, updateAppearance } = useAppearance(); return ( <ToggleGroup type="single" value={appearance} onValueChange={updateAppearance}> <ToggleGroupItem value="light">Light</ToggleGroupItem> <ToggleGroupItem value="dark">Dark</ToggleGroupItem> <ToggleGroupItem value="system">System</ToggleGroupItem> </ToggleGroup> ); } ``` ### Inertia Shared Data Access Access globally shared data from Laravel backend including authentication state, application name, and sidebar state. ```php // Backend: app/Http/Middleware/HandleInertiaRequests.php <?php namespace App\Http\Middleware; use Inertia\Middleware; use Illuminate\Foundation\Inspiring; class HandleInertiaRequests extends Middleware { public function share(Request $request): array { [$message, $author] = str(Inspiring::quotes()->random())->explode('-'); return [ ...parent::share($request), 'name' => config('app.name'), 'quote' => ['message' => trim($message), 'author' => trim($author)], 'auth' => [ 'user' => $request->user(), ], 'sidebarOpen' => !$request->hasCookie('sidebar_state') || $request->cookie('sidebar_state') === 'true', ]; } } // Frontend: Access shared data in any Inertia page component import { usePage } from '@inertiajs/react'; interface SharedProps { name: string; quote: { message: string; author: string }; auth: { user: User | null }; sidebarOpen: boolean; } function WelcomePage() { const { name, auth, quote } = usePage<SharedProps>().props; return ( <div> <h1>Welcome to {name}</h1> {auth.user && <p>Hello, {auth.user.name}!</p>} <blockquote>{quote.message} — {quote.author}</blockquote> </div> ); } ``` ## Configuration and Setup ### Environment Configuration Configure application settings and build-time environment variables for both Laravel backend and React frontend. ```bash # .env file - Laravel configuration APP_NAME="My Application" APP_ENV=local APP_DEBUG=true APP_URL=http://localhost DB_CONNECTION=sqlite DB_DATABASE=/absolute/path/to/database.sqlite # Expose to frontend via Vite VITE_APP_NAME="${APP_NAME}" # Email configuration for password resets and verification MAIL_MAILER=smtp MAIL_HOST=mailpit MAIL_PORT=1025 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null MAIL_FROM_ADDRESS="hello@example.com" MAIL_FROM_NAME="${APP_NAME}" ``` ### Vite Build Configuration Configure frontend build tooling with Laravel plugin, React support with the React Compiler, Tailwind CSS, and Wayfinder for type-safe routing. ```typescript // vite.config.ts import { wayfinder } from '@laravel/vite-plugin-wayfinder'; import tailwindcss from '@tailwindcss/vite'; import react from '@vitejs/plugin-react'; import laravel from 'laravel-vite-plugin'; import { defineConfig } from 'vite'; export default defineConfig({ plugins: [ laravel({ input: ['resources/css/app.css', 'resources/js/app.tsx'], ssr: 'resources/js/ssr.tsx', refresh: true, }), react({ babel: { plugins: ['babel-plugin-react-compiler'], // Automatic React optimization }, }), tailwindcss(), wayfinder({ formVariants: true, // Generate type-safe form helpers }), ], esbuild: { jsx: 'automatic', }, }); // package.json scripts { "scripts": { "dev": "vite", "build": "vite build", "build:ssr": "vite build && vite build --ssr", "format": "prettier --write resources/", "lint": "eslint . --fix", "types": "tsc --noEmit" } } // Development: Run Laravel, Queue, Logs, and Vite concurrently // composer.json { "scripts": { "dev": [ "Composer\\Config::disableProcessTimeout", "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite --kill-others" ], "dev:ssr": [ "npm run build:ssr", "Composer\\Config::disableProcessTimeout", "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"php artisan inertia:start-ssr\" --names=server,queue,logs,ssr --kill-others" ] } } // Start development servers with colored output composer dev ``` ### Fortify Authentication Configuration Configure Laravel Fortify features and behavior for authentication, registration, and two-factor authentication. All authentication features are enabled by default in this starter kit. ```php // config/fortify.php <?php use Laravel\Fortify\Features; return [ 'guard' => 'web', 'passwords' => 'users', 'username' => 'email', 'email' => 'email', 'lowercase_usernames' => true, 'home' => '/dashboard', 'limiters' => [ 'login' => 'login', // 5 requests per minute per email/IP 'two-factor' => 'two-factor', // 5 requests per minute per session ], 'features' => [ Features::registration(), Features::resetPasswords(), Features::emailVerification(), Features::twoFactorAuthentication([ 'confirm' => true, // Require OTP confirmation to enable 2FA 'confirmPassword' => true, // Require password before viewing 2FA settings // 'window' => 0 // Allow codes from previous/next time window ]), ], ]; // Rate limiting configuration: app/Providers/FortifyServiceProvider.php RateLimiter::for('login', function (Request $request) { $throttleKey = Str::transliterate(Str::lower($request->input(Fortify::username())).'|'.$request->ip()); return Limit::perMinute(5)->by($throttleKey); }); RateLimiter::for('two-factor', function (Request $request) { return Limit::perMinute(5)->by($request->session()->get('login.id')); }); // Note: Profile and password updates use custom controllers in this starter kit // instead of Fortify's built-in features for more control ``` ## Summary This Laravel + React starter kit provides a complete foundation for building modern web applications with robust authentication, user management, and security features. The authentication system handles registration, login, password resets, email verification, and two-factor authentication through Laravel Fortify, while the frontend delivers a polished user experience with React 19, TypeScript, and shadcn/ui components. All user-facing authentication flows include proper validation, error handling, rate limiting, and security measures like CSRF protection and session regeneration. The React Compiler automatically optimizes component performance without manual memoization. The integration between Laravel and React via Inertia.js creates a seamless development experience where developers write standard Laravel controllers and React components without building REST APIs or managing complex client-side state. Shared data flows automatically from Laravel middleware to React components, type-safe routing is generated via Laravel Wayfinder, and form submissions work intuitively with server-side validation and Wayfinder's form helpers. Development is streamlined with Vite 7 for instant hot module replacement, Laravel Pail for beautiful colored logs, and concurrent processes for server, queue, logs, and frontend build. This architecture scales from simple CRUD applications to complex SPAs while maintaining developer productivity, code organization, and the best features of both ecosystems. The starter kit serves as an ideal foundation for SaaS applications, internal tools, customer portals, and any web application requiring user authentication and modern UX.