Mastering Inertia v3: A Hands-On Guide to the Next Generation of Single-Page Apps

Overview

represents a significant evolution in how developers bridge the gap between
Laravel
and modern frontend frameworks like
React
and
Vue.js
. Traditionally, building a Single-Page Application (SPA) required maintaining a complex API layer, managing state across two repositories, and handling authentication twice.
Inertia.js
solved this by acting as the glue, allowing you to build SPAs using standard server-side routing and controllers.

Version 3 takes this developer experience further by stripping away boilerplate and introducing features that were previously left to manual implementation. This update focuses on three core pillars: reducing the footprint of the client-side library, improving the feedback loop during development (especially for Server-Side Rendering), and providing first-class utilities for the modern UI patterns users expect, such as optimistic updates and background HTTP requests.

Prerequisites

To follow this guide and implement these features, you should be comfortable with the following:

  • PHP & Laravel: Understanding of routes, controllers, and middleware.
  • Modern JavaScript: Familiarity with ES6 syntax and a frontend framework (
    Vue.js
    or
    React
    ).
  • Vite: Basic knowledge of how
    Vite
    handles asset bundling in a
    Laravel
    context.
  • Inertia Fundamentals: A baseline understanding of how
    Inertia.js
    pages receive data as props from the server.

Key Libraries & Tools

  • Inertia.js
    : The core library and its framework-specific adapters.
  • Laravel
    : The recommended backend framework for the Inertia protocol.
  • Vite
    : The build tool used to compile assets and run the new Inertia plugin.
  • Laravel Wayfinder
    : A tool that provides type-safe routing and form integration between the backend and frontend.
  • Pest
    : Used for browser testing, now with better SSR error reporting.

Section 1: The New Vite Plugin and SSR Development

One of the most immediate changes in v3 is the move toward a cleaner, plugin-driven architecture. In previous versions, your app.js entry point was often cluttered with boilerplate code required to resolve components and set up the application.

explains that the new
Vite
plugin handles this automatically.

Cleaning up app.js

Previously, you had to manually define a resolve callback to tell Inertia where your page components lived. In v3, the

configuration handles this, allowing your entry point to look like this:

import { createInertiaApp } from '@inertiajs/vue3'
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'
import { createApp, h } from 'vue'

createInertiaApp({
  setup({ el, App, props, plugin }) {
    createApp({ render: () => h(App, props) })
      .use(plugin)
      .mount(el)
  },
})

By including the @inertiajs/vite-plugin in your vite.config.js, you no longer need a separate ssr.js entry point. The plugin detects the environment and serves the correct bundle, drastically reducing file noise in your resources/js directory.

Real-Time SSR Debugging

Server-Side Rendering (SSR) is vital for SEO and initial load performance, but it was notoriously difficult to debug locally. In v3, running npm run dev now spins up an SSR development endpoint. When a component fails to render on the server—for example, if you accidentally reference the window object in a server-side context—the error is caught and displayed with a full stack trace directly in your terminal and browser.

// This will now throw a clean SSR error in the console instead of silently crashing
export default {
  setup() {
    if (typeof window !== 'undefined') {
        // Browser only code
    }
    // If you call window.alert() here, v3 provides a source-mapped error trace
  }
}

Section 2: Replacing Axios with useHTTP

In a move to make the library leaner,

v3 has dropped
Axios
as a hard dependency. While
Axios
is powerful, it is often overkill for the specific needs of an Inertia application. By replacing it with a custom XHR implementation, the library shed approximately 100KB from its bundle size.

The useHTTP Hook

To fill the gap for non-navigational requests (like checking a username's availability or toggling a status without changing pages), v3 introduces the useHTTP hook. It mirrors the familiar useForm API, making it intuitive for long-time users.

import { useHTTP } from '@inertiajs/vue3'

const http = useHTTP({
  username: '',
})

const checkAvailability = () => {
  http.post('/check-username', {
    onSuccess: (response) => {
      console.log('Available:', response.data.available)
    },
  })
}

Unlike the standard router.post or useForm.post, this does not trigger a page visit. It returns raw JSON from your controller, allowing for highly interactive UI elements that don't reload the entire page state.

Section 3: Native Optimistic Updates

Perhaps the most requested feature in the v3 release is built-in support for optimistic updates. In modern web apps, users expect instant feedback. If they click a "favorite" button, the icon should turn red immediately, even if the server takes 200ms to process the request.

Implementing Instant Feedback

In v3, you can prepend any request with the .optimistic() method. This method takes a callback where you define what the props should look like assuming the request succeeds.

import { router } from '@inertiajs/vue3'

const toggleFavorite = (id) => {
  router.optimistic((props) => ({
    contacts: props.contacts.map(c => 
      c.id === id ? { ...c, is_favorite: !c.is_favorite } : c
    )
  })).post(`/contacts/${id}/favorite`)
}

If the server returns an error, Inertia automatically rolls back the state to the previous snapshot. This eliminates the need for developers to manually track "loading" or "pending" states for every single toggle switch in their application.

Section 4: Instant Visits and Shared Props

Traditional Inertia visits wait for the server to respond with the new page data before performing the navigation.

change this by navigating to the target component immediately and filling in the data as it arrives.

How Instant Visits Work

When you use an instant visit, you specify which component to render. Inertia will carry over "shared props"—like the authenticated user or the site name—so the layout renders correctly while the specific page content remains in a loading state.

router.visit('/profile', {
  component: 'Profile/Show',
  // Optionally define specific props to carry over
  pageProps: (currentProps) => ({
    user: currentProps.auth.user
  })
})

This is perfect for pages where the data fetching is slow but the UI structure is known, giving the user the feeling of a lightning-fast application.

Syntax Notes

  • Generics for Forms: The useForm hook now supports generics, providing full type-hinting for your form data. This is especially powerful when combined with
    Laravel Wayfinder
    .
  • Event Renaming: Several events have been renamed for clarity. onInvalid is now onHTTPException, and the generic exception event is now onNetworkError (specific to client-side connectivity issues).
  • Layout Prop Access: A new useLayoutProps hook allows page components to pass data directly up to their parent layouts without needing a complex state management library or nested event emitting.

Practical Examples

  1. Travel Booking Dashboards: Use optimistic updates for seat selection or filter toggles where server-side latency is high due to third-party API calls.
  2. Live Search: Use useHTTP to fetch search suggestions in real-time as the user types, without interrupting their scroll position or page state.
  3. Complex Error Pages: Leverage the new handleExceptionsUsing callback in your
    Laravel
    Laravel
    to render custom 404 or 500 pages that still have access to your shared layout data and authenticated user info.

Tips & Gotchas

  • The Server Always Wins: Remember that in optimistic updates, once the real server response arrives, it overwrites the optimistic state. Ensure your frontend logic and backend logic are perfectly synced.
  • SSR Awareness: When using the improved SSR, be careful with third-party libraries that assume a window object exists. Use the onMounted lifecycle hook to run browser-only code.
  • Upgrading: If your project relies heavily on
    Axios
    interceptors for global headers, you must either migrate that logic to Inertia's middleware or manually reinstall
    Axios
    and pass it to the createInertiaApp configuration as an adapter.
7 min read