DEV Community

Cover image for A Practical Guide to Using ReactJS with Inertia.js 2 and Laravel 11
Muhammed Elfeqy
Muhammed Elfeqy

Posted on

A Practical Guide to Using ReactJS with Inertia.js 2 and Laravel 11

ReactJS and Laravel are among the most popular tools for modern web development. Combining React’s dynamic user interfaces with Laravel’s robust backend makes for a highly efficient and scalable stack. Inertia.js acts as the glue that connects the two, offering a smooth way to build single-page applications (SPAs) without sacrificing the simplicity of server-side routing. This guide will walk you through setting up this stack from scratch, creating a CRUD application with file uploads, and enabling advanced features like server-side rendering (SSR).

Why Use React, Inertia.js, and Laravel Together?

  • Benefits:

1- Seamless SPA Development: Inertia allows you to build SPAs without an API layer, reducing boilerplate and simplifying development.

2- Dynamic UI with React: React’s component-based architecture makes building complex user interfaces a breeze.

3- Laravel’s Backend Strength: Laravel’s robust ecosystem provides tools like Eloquent ORM, routing, and task scheduling.

4- Enhanced Developer Experience: Inertia bridges the gap between client-side React and server-side Laravel, enabling effortless data sharing and page navigation.

5- Flexibility with SSR: Inertia’s SSR support ensures faster page loads and improved SEO.


Setting Up Your Environment

Before we dive into building the application, ensure you have the following installed:

  • Node.js: Latest stable version.

  • Composer: To manage PHP dependencies.

  • PHP 8.2+: Required for Laravel 11.

  • Laravel Installer: Optional but recommended.

  • Database: MySQL or PostgreSQL.

Step 1: Create a New Laravel Project

Run the following command to create a new Laravel 11 project:

composer create-project laravel/laravel laravel-inertia-react --prefer-dist
cd laravel-inertia-react
Enter fullscreen mode Exit fullscreen mode

Step 2: Install Inertia.js

Inertia.js has both a server-side adapter for Laravel and client-side adapters for JavaScript frameworks like React.

Install the Laravel Adapter:

composer require inertiajs/inertia-laravel
Enter fullscreen mode Exit fullscreen mode

Install the React Client Adapter:

npm install @inertiajs/react
Enter fullscreen mode Exit fullscreen mode

Step 3: Configure Inertia Middleware

In Laravel 11, Inertia middleware is automatically registered. However, you may customize the middleware if necessary by editing app/Http/Middleware/HandleInertiaRequests.php.

Make sure the middleware is included in the bootstrap/app.php withMiddleware method:

<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

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: [
            \App\Http\Middleware\HandleInertiaRequests::class,
        ]);

    })
Enter fullscreen mode Exit fullscreen mode

Step 4: Install React and Vite

Laravel uses Vite for modern frontend tooling. Install React and its dependencies:

npm install react react-dom
npm install @vitejs/plugin-react
Enter fullscreen mode Exit fullscreen mode

Update your vite.config.js:

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

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

Create the entry file at resources/js/app.jsx:

import React from 'react';
import { createRoot } from 'react-dom/client';
import { createInertiaApp } from '@inertiajs/react';

createInertiaApp({
    resolve: name => import(`./Pages/${name}`),
    setup({ el, App, props }) {
        const root = createRoot(el);
        root.render(<App {...props} />);
    },
});
Enter fullscreen mode Exit fullscreen mode

Update your resources/views/app.blade.php layout:

<!DOCTYPE html>
<html>
<head>
    @vite('resources/js/app.jsx')
</head>
<body>
    @inertia
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Step 5: Enable SSR

Install the SSR adapter for Inertia: Server-side rendering (SSR) is particularly beneficial in this stack as it improves SEO by providing search engines with fully-rendered pages. Additionally, SSR enhances initial load times for users, creating a smoother experience.

npm install @inertiajs/server
Enter fullscreen mode Exit fullscreen mode

Update your vite.config.js to enable SSR:

ssr: {
    noExternal: ['@inertiajs/server']
},
Enter fullscreen mode Exit fullscreen mode

Run the server:

php artisan serve
npm run dev
Enter fullscreen mode Exit fullscreen mode

Building a CRUD Application

Let’s create a CRUD (Create, Read, Update, Delete) application to manage "Posts," complete with file uploads and validation. CRUD operations form the backbone of most web applications, enabling essential data management workflows. File uploads are included to demonstrate how to handle media or document uploads, a common feature in modern applications.

Step 1: Create the Database and Models

Update your .env file with your database credentials. Then, create the migration for the posts table:

php artisan make:model Post -m
Enter fullscreen mode Exit fullscreen mode

Edit the migration file in database/migrations:

public function up()
{
    Schema::create('posts', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->text('content');
        $table->string('file_path')->nullable();
        $table->timestamps();
    });
}
Enter fullscreen mode Exit fullscreen mode

Run the migration:

php artisan migrate
Enter fullscreen mode Exit fullscreen mode

Step 2: Create a Controller

Generate the controller for posts:

php artisan make:controller PostController
Enter fullscreen mode Exit fullscreen mode

In PostController, implement CRUD methods:

use App\Models\Post;
use Illuminate\Http\Request;
use Inertia\Inertia;

class PostController extends Controller
{
    public function index()
    {
        return Inertia::render('Posts/Index', [
            'posts' => Post::all(),
        ]);
    }

    public function create()
    {
        return Inertia::render('Posts/Create');
    }

    public function store(Request $request)
    {
        $validated = $request->validate([
            'title' => 'required|string|max:255',
            'content' => 'required|string',
            'file' => 'nullable|file',
        ]);

        $filePath = $request->file('file')?->store('uploads', 'public');

        Post::create(array_merge($validated, [
            'file_path' => $filePath,
        ]));

        return redirect()->route('posts.index');
    }

    public function edit(Post $post)
    {
        return Inertia::render('Posts/Edit', [
            'post' => $post,
        ]);
    }

    public function update(Request $request, Post $post)
    {
        $validated = $request->validate([
            'title' => 'required|string|max:255',
            'content' => 'required|string',
            'file' => 'nullable|file',
        ]);

        $filePath = $request->file('file')?->store('uploads', 'public');

        $post->update(array_merge($validated, [
            'file_path' => $filePath,
        ]));

        return redirect()->route('posts.index');
    }

    public function destroy(Post $post)
    {
        $post->delete();
        return redirect()->route('posts.index');
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Define Routes

Update your routes/web.php file:

use App\Http\Controllers\PostController;

Route::resource('posts', PostController::class);
Enter fullscreen mode Exit fullscreen mode

Step 4: Create React Pages

resources/js/Pages/Posts/Index.jsx:

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

export default function Index() {
    const { posts } = usePage().props;

    return (
        <div>
            <Link href="/posts/create">Create Post</Link>
            <ul>
                {posts.map(post => (
                    <li key={post.id}>
                        {post.title}
                        <Link href={`/posts/${post.id}/edit`}>Edit</Link>
                    </li>
                ))}
            </ul>
        </div>
    );
}
Enter fullscreen mode Exit fullscreen mode

Other Components

For example, in resources/js/Pages/Posts/Create.jsx:

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

export default function Create() {
    const { data, setData, post, errors } = useForm({
        title: '',
        content: '',
        file: null,
    });

    const handleSubmit = (e) => {
        e.preventDefault();
        post('/posts');
    };

    return (
        <form onSubmit={handleSubmit}>
            <div>
                <label>Title</label>
                <input
                    type="text"
                    value={data.title}
                    onChange={(e) => setData('title', e.target.value)}
                />
                {errors.title && <div>{errors.title}</div>}
            </div>
            <div>
                <label>Content</label>
                <textarea
                    value={data.content}
                    onChange={(e) => setData('content', e.target.value)}
                ></textarea>
                {errors.content && <div>{errors.content}</div>}
            </div>
            <div>
                <label>File</label>
                <input
                    type="file"
                    onChange={(e) => setData('file', e.target.files[0])}
                />
                {errors.file && <div>{errors.file}</div>}
            </div>
            <button type="submit">Create</button>
        </form>
    );
}
Enter fullscreen mode Exit fullscreen mode

Similarly, in resources/js/Pages/Posts/Edit.jsx:

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

export default function Edit({ post }) {
    const { data, setData, put, errors } = useForm({
        title: post.title,
        content: post.content,
        file: null,
    });

    const handleSubmit = (e) => {
        e.preventDefault();
        put(`/posts/${post.id}`);
    };

    return (
        <form onSubmit={handleSubmit}>
            <div>
                <label>Title</label>
                <input
                    type="text"
                    value={data.title}
                    onChange={(e) => setData('title', e.target.value)}
                />
                {errors.title && <div>{errors.title}</div>}
            </div>
            <div>
                <label>Content</label>
                <textarea
                    value={data.content}
                    onChange={(e) => setData('content', e.target.value)}
                ></textarea>
                {errors.content && <div>{errors.content}</div>}
            </div>
            <div>
                <label>File</label>
                <input
                    type="file"
                    onChange={(e) => setData('file', e.target.files[0])}
                />
                {errors.file && <div>{errors.file}</div>}
            </div>
            <button type="submit">Update</button>
        </form>
    );
}
Enter fullscreen mode Exit fullscreen mode

These examples provide basic forms for creating and editing posts, including error handling and file upload support.


Conclusion

This guide demonstrates how to set up a Laravel 11 and ReactJS application using Inertia.js 2. The stack simplifies SPA development, minimizes complexity, and allows for features like SSR. With this foundational setup, you can build dynamic and scalable web applications efficiently.

Top comments (0)