How to Use Inertia.js with Laravel: A Simple Guide for Beginners

Imagine building a super-fast website that feels like a mobile app, but you don't need to learn complicated APIs or extra tools! That's exactly what Inertia.js helps you do. In this guide, we'll learn how to connect Laravel (the backend) with React, Vue, or Svelte (the frontend) using Inertia.js. Even if you're just starting out in web development, this tutorial will make everything clear and simple!

Inertia.js with Laravel Tutorial

What is Inertia.js? (Explained Simply)

Think of Inertia.js as a bridge between your Laravel backend and your React/Vue frontend. Normally, when you want to build a modern website with Laravel and React, you'd need to:

  • Create a separate API (like a restaurant menu that lists all available dishes)
  • Write extra code to send data back and forth
  • Handle authentication twice (once in Laravel, once in React)

With Inertia.js, you don't need any of that! It lets you build modern websites using Laravel for routing and controllers (like you normally would), but renders your pages using React, Vue, or Svelte components. It's like having the best of both worlds! 🎉

🎯 Real-Life Example:

Imagine you're building a school website. With Inertia.js, your Laravel backend handles everything (student login, fetching grades, saving homework), and your React frontend makes it look beautiful and interactive—all without building a separate API!


Why Should You Use Inertia.js?

Here are the awesome benefits of using Inertia.js:

✅ No API Needed

You don't have to create RESTful or GraphQL APIs. Inertia handles communication for you!

⚡ Super Fast

Pages load instantly because only the data changes, not the whole page.

🔒 One Authentication System

Use Laravel's built-in authentication. No need to duplicate login logic in React.

📱 SPA Feel

Your website feels like a mobile app with smooth page transitions.


What You Need Before Starting

Before we begin, make sure you have these installed on your computer:

  • PHP 8.2+ (Laravel's language)
  • Composer (PHP package manager - like a app store for PHP tools)
  • Node.js & NPM (JavaScript package manager - for React/Vue)
  • MySQL or any database (to store your data)
  • A code editor like VS Code

Don't worry if you don't understand everything yet. Just make sure these are installed!


Step 1: Create a New Laravel Project

First, let's create a fresh Laravel project. Open your terminal (command prompt) and type this command:

composer create-project laravel/laravel inertia-app

This creates a folder called inertia-app with all Laravel files inside. Think of it like creating a new folder for your school project! 📁

Now, go into that folder:

cd inertia-app

Step 2: Install Inertia.js in Laravel (Server-Side)

Now we need to add Inertia.js to our Laravel project. Run this command:

composer require inertiajs/inertia-laravel

What does this do? This command installs the Inertia package for Laravel. It's like adding a special tool to your toolbox that helps Laravel talk to React or Vue.

Create the Root Template

Inertia needs one main HTML file that will load your React/Vue components. Let's create it! Go to resources/views folder and create a file called app.blade.php:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>My Inertia App</title>

    <!-- Load our CSS and JavaScript files -->
    @vite(['resources/css/app.css', 'resources/js/app.jsx'])
    @inertiaHead
</head>
<body>
    @inertia
</body>
</html>

Think of this like: This is your website's main container. The @inertia part is like a magic box where all your React/Vue pages will appear!

Set Up Inertia Middleware

Middleware is like a security guard that checks things before they enter your website. Let's publish Inertia's middleware:

php artisan inertia:middleware

Now open bootstrap/app.php and add the middleware to the web group:

use App\Http\Middleware\HandleInertiaRequests;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->web(append: [
            HandleInertiaRequests::class,
        ]);
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();

Step 3: Install Inertia.js for React (Client-Side)

Now let's add Inertia to the React side! We'll use React in this example, but you can also use Vue or Svelte.

npm install @inertiajs/react react react-dom

What's happening? This installs React and the Inertia React adapter. It's like downloading apps on your phone—these are the tools React needs to work with Inertia!

Install Vite Plugin

Vite is a tool that bundles your JavaScript code. We need the Inertia plugin for it:

npm install @vitejs/plugin-react

Configure Vite

Open vite.config.js and update it:

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import react from '@vitejs/plugin-react';

export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/css/app.css', 'resources/js/app.jsx'],
            refresh: true,
        }),
        react(),
    ],
});

Step 4: Set Up Your React Application

Now let's create the main React file that will connect everything together. Go to resources/js and rename app.js to app.jsx, then replace its content with:

import { createRoot } from 'react-dom/client';
import { createInertiaApp } from '@inertiajs/react';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';

createInertiaApp({
    // This tells Inertia where to find your React page components
    resolve: (name) => 
        resolvePageComponent(
            `./Pages/${name}.jsx`, 
            import.meta.glob('./Pages/**/*.jsx')
        ),
    
    // This sets up your app
    setup({ el, App, props }) {
        createRoot(el).render(<App {...props} />);
    },
});

Simple explanation: This code tells React: "Hey, when someone visits a page, look in the Pages folder for the matching component and show it!"


Step 5: Create Your First React Page Component

Let's create a simple homepage! Create a new folder: resources/js/Pages, then inside it, create Home.jsx:

import React from 'react';
import { Head, Link } from '@inertiajs/react';

export default function Home({ message, userName }) {
    return (
        <>
            <Head title="Welcome to Inertia.js" />
            
            <div className="min-h-screen bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center">
                <div className="bg-white rounded-lg shadow-2xl p-8 max-w-md">
                    <h1 className="text-4xl font-bold text-gray-800 mb-4">
                        🎉 {message}
                    </h1>
                    
                    <p className="text-xl text-gray-600 mb-6">
                        Hello, <strong>{userName}</strong>! Welcome to your first Inertia.js app!
                    </p>
                    
                    <Link 
                        href="/about" 
                        className="bg-blue-500 text-white px-6 py-3 rounded-lg hover:bg-blue-600 transition"
                    >
                        Go to About Page →
                    </Link>
                </div>
            </div>
        </>
    );
}

What's special here? Notice how we receive message and userName as props? These come directly from Laravel! Also, the Link component makes navigation super smooth without page reloads.


Step 6: Create Laravel Routes and Controllers

Now let's tell Laravel: "When someone visits the homepage, show them the Home component." Open routes/web.php:

<?php

use Illuminate\Support\Facades\Route;
use Inertia\Inertia;

// Home page route
Route::get('/', function () {
    return Inertia::render('Home', [
        'message' => 'Welcome to Inertia.js!',
        'userName' => 'Student',
    ]);
});

// About page route
Route::get('/about', function () {
    return Inertia::render('About', [
        'title' => 'About Us',
        'description' => 'This is a simple Inertia.js application built with Laravel and React!',
    ]);
});

See what happened? Inertia::render('Home', [...]) tells Laravel: "Render the Home.jsx component and send this data to it." No API needed! 🚀

Create the About Page

Let's create resources/js/Pages/About.jsx:

import React from 'react';
import { Head, Link } from '@inertiajs/react';

export default function About({ title, description }) {
    return (
        <>
            <Head title={title} />
            
            <div className="min-h-screen bg-gradient-to-br from-green-500 to-teal-600 flex items-center justify-center">
                <div className="bg-white rounded-lg shadow-2xl p-8 max-w-md">
                    <h1 className="text-3xl font-bold text-gray-800 mb-4">
                        {title}
                    </h1>
                    
                    <p className="text-lg text-gray-600 mb-6">
                        {description}
                    </p>
                    
                    <Link 
                        href="/" 
                        className="bg-green-500 text-white px-6 py-3 rounded-lg hover:bg-green-600 transition"
                    >
                        ← Back to Home
                    </Link>
                </div>
            </div>
        </>
    );
}

Step 7: Run Your Application

Time to see your app in action! Open two terminal windows:

Terminal 1: Start Laravel Server

php artisan serve

This runs your Laravel backend at http://localhost:8000

Terminal 2: Start Vite Dev Server

npm run dev

This watches your React files and automatically updates when you make changes. It's like having a helper that rebuilds your code instantly!

🎊 Congratulations!

Open your browser and visit http://localhost:8000. You should see your beautiful homepage! Try clicking the "About" link—notice how the page changes smoothly without reloading? That's Inertia.js magic! ✨


Step 8: Working with Forms in Inertia.js

Let's learn how to submit forms! Inertia makes this super easy. Create a new page resources/js/Pages/Contact.jsx:

import React from 'react';
import { Head, useForm } from '@inertiajs/react';

export default function Contact() {
    // useForm hook manages form state for us
    const { data, setData, post, processing, errors } = useForm({
        name: '',
        email: '',
        message: '',
    });

    const handleSubmit = (e) => {
        e.preventDefault();
        // This sends data to Laravel without page reload!
        post('/contact');
    };

    return (
        <>
            <Head title="Contact Us" />
            
            <div className="min-h-screen bg-gray-100 py-12 px-4">
                <div className="max-w-md mx-auto bg-white rounded-lg shadow-md p-8">
                    <h1 className="text-3xl font-bold mb-6">Contact Us</h1>
                    
                    <form onSubmit={handleSubmit}>
                        <div className="mb-4">
                            <label className="block text-gray-700 mb-2">Name:</label>
                            <input
                                type="text"
                                value={data.name}
                                onChange={e => setData('name', e.target.value)}
                                className="w-full border border-gray-300 rounded px-4 py-2"
                            />
                            {errors.name && (
                                <p className="text-red-500 text-sm mt-1">{errors.name}</p>
                            )}
                        </div>

                        <div className="mb-4">
                            <label className="block text-gray-700 mb-2">Email:</label>
                            <input
                                type="email"
                                value={data.email}
                                onChange={e => setData('email', e.target.value)}
                                className="w-full border border-gray-300 rounded px-4 py-2"
                            />
                            {errors.email && (
                                <p className="text-red-500 text-sm mt-1">{errors.email}</p>
                            )}
                        </div>

                        <div className="mb-6">
                            <label className="block text-gray-700 mb-2">Message:</label>
                            <textarea
                                value={data.message}
                                onChange={e => setData('message', e.target.value)}
                                className="w-full border border-gray-300 rounded px-4 py-2"
                                rows="4"
                            />
                            {errors.message && (
                                <p className="text-red-500 text-sm mt-1">{errors.message}</p>
                            )}
                        </div>

                        <button
                            type="submit"
                            disabled={processing}
                            className="w-full bg-blue-500 text-white py-3 rounded hover:bg-blue-600 transition disabled:bg-gray-400"
                        >
                            {processing ? 'Sending...' : 'Send Message'}
                        </button>
                    </form>
                </div>
            </div>
        </>
    );
}

Now add the route in routes/web.php:

Route::get('/contact', function () {
    return Inertia::render('Contact');
});

Route::post('/contact', function (Request $request) {
    // Validate the form data
    $validated = $request->validate([
        'name' => 'required|min:3',
        'email' => 'required|email',
        'message' => 'required|min:10',
    ]);
    
    // In real app, save to database or send email
    // For now, just redirect back with success message
    
    return redirect()->back()->with('success', 'Message sent successfully!');
});

What's cool here? The useForm hook handles everything: form state, submission, loading states, and even displays Laravel's validation errors automatically!


Step 9: Share Data Across All Pages

Sometimes you want to show the same data on every page (like the logged-in user's name). Open app/Http/Middleware/HandleInertiaRequests.php:

public function share(Request $request): array
{
    return [
        ...parent::share($request),
        
        // This data is available on ALL pages!
        'auth' => [
            'user' => $request->user() ? [
                'name' => $request->user()->name,
                'email' => $request->user()->email,
            ] : null,
        ],
        
        // Flash messages (success/error notifications)
        'flash' => [
            'success' => $request->session()->get('success'),
            'error' => $request->session()->get('error'),
        ],
    ];
}

Now in ANY React component, you can access this shared data using the usePage hook:

import { usePage } from '@inertiajs/react';

export default function MyComponent() {
    const { auth, flash } = usePage().props;
    
    return (
        <div>
            {auth.user && <p>Welcome, {auth.user.name}!</p>}
            {flash.success && <div className="alert">{flash.success}</div>}
        </div>
    );
}

Step 10: Create a Reusable Layout

Most websites have a common header and footer. Let's create a layout component! Create resources/js/Layouts/MainLayout.jsx:

import React from 'react';
import { Link, usePage } from '@inertiajs/react';

export default function MainLayout({ children }) {
    const { auth } = usePage().props;

    return (
        <div className="min-h-screen bg-gray-50">
            {/* Header/Navigation */}
            <nav className="bg-white shadow-md">
                <div className="max-w-7xl mx-auto px-4 py-4 flex justify-between items-center">
                    <Link href="/" className="text-2xl font-bold text-blue-600">
                        MyApp
                    </Link>
                    
                    <div className="space-x-4">
                        <Link href="/" className="text-gray-700 hover:text-blue-600">
                            Home
                        </Link>
                        <Link href="/about" className="text-gray-700 hover:text-blue-600">
                            About
                        </Link>
                        <Link href="/contact" className="text-gray-700 hover:text-blue-600">
                            Contact
                        </Link>
                    </div>

                    <div>
                        {auth.user ? (
                            <span className="text-gray-700">
                                Hello, {auth.user.name}
                            </span>
                        ) : (
                            <Link href="/login" className="bg-blue-500 text-white px-4 py-2 rounded">
                                Login
                            </Link>
                        )}
                    </div>
                </div>
            </nav>

            {/* Main Content */}
            <main className="max-w-7xl mx-auto py-8 px-4">
                {children}
            </main>

            {/* Footer */}
            <footer className="bg-gray-800 text-white py-6 mt-12">
                <div className="max-w-7xl mx-auto px-4 text-center">
                    <p>© 2025 MyApp. Built with Laravel & Inertia.js</p>
                </div>
            </footer>
        </div>
    );
}

Now wrap your pages with this layout:

import MainLayout from '@/Layouts/MainLayout';

export default function Home({ message, userName }) {
    return (
        <MainLayout>
            <div className="bg-white rounded-lg shadow-md p-8">
                <h1 className="text-4xl font-bold">{message}</h1>
                <p>Hello, {userName}!</p>
            </div>
        </MainLayout>
    );
}

Advanced Features (Optional)

1. Prefetching Links

Make your app even faster by loading page data before the user clicks:

<Link href="/about" prefetch="hover">
    About Us
</Link>

2. Partial Reloads

Only reload specific parts of your data:

import { router } from '@inertiajs/react';

// Only reload 'posts' data, keep everything else the same
router.reload({ only: ['posts'] });

3. Progress Indicator

Show a loading bar when navigating between pages. Add this to resources/js/app.jsx:

import { router } from '@inertiajs/react';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';

router.on('start', () => NProgress.start());
router.on('finish', () => NProgress.done());

Don't forget to install nprogress:

npm install nprogress

Common Problems and Solutions

❌ Problem: "Vite manifest not found"

Solution: Make sure you ran npm run dev in a separate terminal. Both Laravel server AND Vite must be running during development.

❌ Problem: Page not found

Solution: Check that your component name matches the route. If your route says Inertia::render('Home'), your file must be Pages/Home.jsx.

❌ Problem: Data not showing up

Solution: Make sure you're passing data from Laravel correctly and receiving it as props in React. Check the browser console for errors.

❌ Problem: Forms not submitting

Solution: Check that your route matches (GET for displaying form, POST for submitting). Also verify you're using post('/route') in your form, not get().


Best Practices for Using Inertia.js

✅ DO:

  • • Use shared data for common information
  • • Create reusable layout components
  • • Validate forms on the server (Laravel)
  • • Use Inertia's form helper for easy submission
  • • Keep components organized in folders

❌ DON'T:

  • • Don't create separate APIs for Inertia
  • • Don't use window.location for navigation
  • • Don't mix Inertia routes with API routes
  • • Don't forget CSRF protection for forms
  • • Don't skip validation on the backend

Conclusion: You Did It! 🎉

Congratulations! You've just learned how to build modern web applications using Inertia.js with Laravel and React. Let's recap what you've learned:

  • ✅ What Inertia.js is and why it's awesome
  • ✅ How to install and configure Inertia.js in Laravel
  • ✅ How to create React page components
  • ✅ How to pass data from Laravel to React
  • ✅ How to handle forms and validation
  • ✅ How to share data across all pages
  • ✅ How to create reusable layouts

Remember: Inertia.js is like a bridge that connects your Laravel backend with your React frontend. You don't need to build APIs, and you can use Laravel for everything you're already familiar with!

🚀 Next Steps:

  • • Try building a simple blog or todo app
  • • Explore Inertia.js official documentation at inertiajs.com
  • • Learn about authentication with Laravel Breeze + Inertia
  • • Experiment with Vue or Svelte instead of React
  • • Join the Inertia.js community on Discord

Keep practicing, keep building, and most importantly—have fun! If you found this guide helpful, share it with your friends who are learning web development. Happy coding! 💻✨


Frequently Asked Questions

What is Inertia.js and why should I use it?+
Do I need to create an API when using Inertia.js with Laravel?+
Can I use Inertia.js with React, Vue, or Svelte?+
How does authentication work with Inertia.js?+
What is the difference between Inertia.js and Next.js?+
How do forms work in Inertia.js?+
Is Inertia.js good for SEO?+
What is shared data in Inertia.js?+
Can I use Inertia.js with an existing Laravel project?+
How do I navigate between pages in Inertia.js?+
What are partial reloads in Inertia.js?+
Do I need both Laravel server and Vite running during development?+
What is the HandleInertiaRequests middleware?+
Can I use Tailwind CSS with Inertia.js and Laravel?+
How do I handle file uploads with Inertia.js?+
What's the difference between Inertia::render() and returning JSON?+
Can I prefetch pages in Inertia.js for faster navigation?+
How do I show loading indicators during page transitions?+
What happens if my Inertia component name doesn't match my route?+
Is Inertia.js suitable for large applications?+

Helpful Resources