Originally published at hafiz.dev
Inertia.js v3 went stable on March 25. If you missed the announcement, it's a real major release, not one of those "major" bumps that changes nothing. Axios is gone, ESM is the only output format, React 18 and Svelte 4 are both dropped, and a handful of event names and APIs have changed. There's also a config file restructure that'll catch you off guard if you don't read the upgrade guide first.
The good news: v3 is genuinely better. The bundle is smaller, SSR works in dev without a separate Node.js process, and two new APIs (the useHttp hook and optimistic updates) solve problems that previously needed awkward workarounds. If you've been putting off upgrading to take stock of the situation first, this post is for you.
We also just went through a similar exercise with the Laravel 12 to 13 upgrade, which had zero breaking changes. Inertia v3 is different. Worth actually reading before you touch anything.
What's Actually New in v3
Before we get into what breaks, let's talk about why you'd want to upgrade at all.
The Vite plugin. This is the biggest quality-of-life change. Previously, setting up an Inertia app meant writing a resolve callback, a setup callback, and a separate SSR entry point with its own config. Now you just install @inertiajs/vite and your entry point can be a single createInertiaApp() call with no arguments. Page resolution, code splitting, and SSR config all happen automatically. It removes a surprising amount of boilerplate that you'd copy-paste from the docs and forget about for years.
SSR in dev mode. Before v3, if you were running SSR, you had to start a separate Node.js server to see it during development. Now the Vite plugin handles it automatically as part of npm run dev. No extra process, better error messages (it logs the component name and URL when SSR fails), and a flash-of-unstyled-content fix is included.
The useHttp hook. This one fills a genuine gap. In v2, if you needed to make an HTTP request that didn't trigger a page visit, like hitting a search endpoint or submitting to an API route, you'd reach for Axios or raw fetch and lose the reactive state you get from useForm. The new useHttp hook gives you the same developer experience as useForm (reactive processing, errors, progress, isDirty state) but for plain JSON requests. No page navigation, no full Inertia lifecycle.
// Vue example
const http = useHttp({ query: '' })
function search() {
http.get('/api/search', { query: http.data.query })
}
If you've been mixing Axios calls inside Inertia components because there was no clean alternative, this replaces that pattern entirely. Pairs nicely with the REST API patterns covered here if you're calling your own endpoints.
Optimistic updates. Inertia now has first-class support for applying a UI change immediately before the server confirms it, then rolling it back automatically if the request fails. It works on the router, useForm, and useHttp. Concurrent optimistic requests are handled too, each with its own rollback snapshot.
router
.optimistic((props) => ({
post: { ...props.post, likes: props.post.likes + 1 },
}))
.post(`/posts/${post.id}/like`)
Before v3, building this pattern meant managing local state manually, writing your own rollback logic, and being careful about race conditions. Now it's one chained method call.
Layout props. You can now pass typed data from a page component into its persistent layout without needing an event bus or provide/inject. Pages declare layout props alongside the layout component, and the layout receives them as regular component props. Much cleaner than the workarounds people were using.
Instant visits. When navigating, Inertia can now swap to the target page component immediately using shared props, then merge in the page-specific props once the server responds. The navigation feels instant even though a full server request still happens. Opt in per-link with :instant or globally via config.
Can You Upgrade Right Now?
Before reading any further, this diagram gives you the quick answer based on your setup.
If the diagram lands you at "Upgrade now", the rest of this guide is your step-by-step path. If you're in one of the "migrate first" branches, come back once that's done. This post isn't going anywhere.
Before You Run composer update: Check Your Requirements
This is the part that'll bite you if you skip it. Inertia v3 has hard version requirements that you need to meet before installing anything.
PHP 8.2 and Laravel 11 are the minimum. If you're on Laravel 10 or PHP 8.1, you need to upgrade those first. Laravel 13 is fully compatible and has zero issues with Inertia v3. The Laravel adapter was tested against both L12 and L13 and there are no compatibility problems to worry about on the PHP side.
React 19 is required if you're using the React adapter. React 18 is no longer supported. This is the requirement most likely to cause a ripple effect through your dependency tree, since React 19 also requires updating things like react-dom, form libraries, and animation packages.
Svelte 5 is required for the Svelte adapter. Svelte 4 is dropped entirely. All Svelte code needs to be updated to Svelte 5's runes syntax: $props(), $state(), $effect(), and so on. This isn't a small change. If you're on Svelte 4, factor in real migration time.
Vite 7+ is required. Vite 6 is no longer supported. If you're on Vite 8 already, you're fine. If you're somewhere behind, check your package.json first.
Vue 3 users? You don't have any of these extra adapter concerns. The Vue adapter upgrade is the cleanest path.
The Upgrade Steps
With requirements confirmed, here's the actual upgrade sequence. Don't skip the last two commands, they're not optional.
One thing to check before you start: if you're using third-party Inertia packages like Inertia Modal, Inertia Table, or any community adapters, verify they have v3 support before upgrading. Some packages ship separate major versions for Inertia v3 compatibility (Inertia Table v3 for example targets Tailwind v4). Check each package's GitHub releases page. There's no point upgrading Inertia core if a critical package in your app isn't ready yet.
# Install the client adapter (pick your framework)
npm install @inertiajs/vue3@^3.0
# or: npm install @inertiajs/react@^3.0
# or: npm install @inertiajs/svelte@^3.0
# optional but recommended: install the Vite plugin
npm install @inertiajs/vite@^3.0
# upgrade the Laravel adapter
composer require inertiajs/inertia-laravel:^3.0
# republish the config file (it has been restructured in v3)
php artisan vendor:publish --provider="Inertia\\ServiceProvider" --force
# clear cached views (@inertia directive output has changed)
php artisan view:clear
After the package installs, work through this checklist before you test anything. It covers every breaking change you'll need to handle.
Breaking Changes to Fix
Here's the full breakdown of what needs changing, with before/after code for each.
Event renames
Two global router events have been renamed. If you've got router.on() listeners anywhere in your app, search for both of these.
// Before (v2)
router.on('invalid', (event) => { /* ... */ })
router.on('exception', (event) => { /* ... */ })
// After (v3)
router.on('httpException', (event) => { /* ... */ })
router.on('networkError', (event) => { /* ... */ })
You can also handle these per-visit now using onHttpException and onNetworkError callbacks, which didn't exist in v2.
Axios, qs, and lodash-es are gone
Inertia no longer bundles any of these. For most apps this means nothing changes, because you weren't importing them directly from Inertia's internals. But if any of your code does import axios from 'axios', import qs from 'qs', or import _ from 'lodash-es' and those packages were implicit transitive dependencies, you'll need to install them directly.
npm install axios # if you still need it
npm install qs # if your code imports it directly
npm install lodash-es # if your code imports it directly
Axios is still usable, just not required. The built-in XHR client supports interceptors natively, so if you were using Axios interceptors you can migrate them directly.
The inertia head attribute became data-inertia
Open your root Blade template and look in the <head> section. Any element with the inertia attribute needs it renamed.
<!-- Before (v2) -->
<title inertia>My App</title>
<!-- After (v3) -->
<title data-inertia>My App</title>
Small change, easy to miss. Affects any head element you're managing with Inertia's <Head> component.
router.cancel() became router.cancelAll()
// Before (v2): only cancelled synchronous requests
router.cancel()
// After (v3): cancels all request types by default
router.cancelAll()
// To match v2 behavior exactly (sync only)
router.cancelAll({ async: false, prefetch: false })
Inertia::lazy() is removed
If you were using Inertia::lazy() on any backend response, switch it to Inertia::optional(). Same behaviour, different name. The LazyProp class is also removed.
// Before (v2)
return Inertia::render('Users/Index', [
'users' => Inertia::lazy(fn () => User::all()),
]);
// After (v3)
return Inertia::render('Users/Index', [
'users' => Inertia::optional(fn () => User::all()),
]);
Progress indicator exports
// Before (v2)
import { hideProgress, revealProgress } from '@inertiajs/vue3'
hideProgress()
revealProgress()
// After (v3)
import { progress } from '@inertiajs/vue3'
progress.hide()
progress.reveal()
The future config block is gone
If you were using v2's future options in createInertiaApp, just delete the entire future block. All four options are now always enabled and can't be toggled.
// Before (v2)
createInertiaApp({
defaults: {
future: {
preserveEqualProps: true,
useDataInertiaHeadAttribute: true,
},
},
})
// After (v3): just remove it
createInertiaApp({
// ...
})
Config file restructuring
After running vendor:publish --force, open the new config/inertia.php and compare it side by side with your old one. Page-related settings have moved under a pages key, and the testing section is simplified. Don't just overwrite and hope. Review the diff. The Diff Checker tool is handy for this if you saved a copy of your old config.
ESM-only output
All Inertia packages now ship as ES Modules only. CommonJS require() imports no longer work.
// Before (v2): worked in some setups
const { router } = require('@inertiajs/vue3')
// After (v3): ESM only
import { router } from '@inertiajs/vue3'
If your build setup was relying on CommonJS in any way, this is the one to audit carefully.
Two Things Worth Using Right Away
Once the upgrade is done, two features are worth reaching for immediately.
useHttp for non-navigation requests. If you're building a Laravel + Vue SPA and have any search boxes, autocomplete fields, or background data fetches that aren't page visits, replace them with useHttp. You get reactive state, proper error handling, and upload progress for free. No more mixing Axios calls into Inertia components.
Optimistic updates for interactive actions. Like buttons, follow buttons, toggles, anything where the user takes an action and you're confident it'll succeed. Chain .optimistic() before the request, define the expected state change, and let Inertia handle the rollback if something goes wrong. It's the kind of UX improvement that takes days to implement correctly from scratch and ten minutes with v3.
If you're also using Wayfinder with Inertia for type-safe routing, the Laravel Wayfinder guide here is worth revisiting since Inertia v3's typed form generics pair well with Wayfinder's typed route parameters.
Should You Upgrade This Weekend?
Depends on your stack.
Upgrade now if you're on Vue 3, Laravel 11+, PHP 8.2+, and Vite 7+. The breaking changes are real but mechanical. Search and replace, rename two or three methods, republish the config, clear views. Most of the checklist items above take less than five minutes each. You'll be done in an afternoon. The Vite plugin alone makes it worth it, and the removed Axios dependency is a free bundle size reduction you'd otherwise have to work for.
Wait if you're on the React adapter and haven't upgraded to React 19. That's a separate, larger upgrade and you shouldn't do both at once. Get your React 19 migration done first, make sure everything still works, then layer in Inertia v3. Same story for Svelte 4 apps. The Svelte 5 runes migration is a real rewrite, not a find-and-replace. Don't combine it with an Inertia upgrade in the same PR.
Don't rush if you're running a production app with SSR and you haven't got a proper staging environment to test on. The SSR improvements in v3 are genuinely good, but SSR changes are also the most likely to surface behaviour differences between environments. Test on staging, watch it for a day, then deploy.
One more thing worth knowing: Laravel Boost ships with an UpgradeInertiaV3 prompt if you're using it. It walks through the upgrade automatically. Worth checking before you do it manually.
v2 isn't going anywhere immediately, so there's no pressure. But v3 is the better version, and if your requirements are already met, there's no reason to sit on it.
Frequently Asked Questions
Do I have to use the new Vite plugin?
No. The @inertiajs/vite plugin is optional. Your existing setup with resolve and setup callbacks still works in v3. The plugin just simplifies things if you want it to.
Can I still use Axios with Inertia v3?
Yes. Axios is no longer bundled or required, but it's still available as an optional peer dependency. Install it manually and use the Axios adapter if you prefer to keep your existing interceptor setup.
What if I'm on Laravel 10?
The Laravel adapter v3 requires Laravel 11 at minimum. You'd need to upgrade Laravel first. Laravel 10 reaches end of life in August 2026, so the upgrade is worth doing regardless.
Does Inertia v3 work with Filament?
Filament uses Livewire under the hood, not Inertia. The two don't interact. If you're building an admin panel with Filament alongside an Inertia-powered frontend, the Inertia upgrade doesn't affect Filament at all.
Is the useHttp hook available in all three adapters?
Yes. Vue, React, and Svelte all have it. The API is the same across adapters: reactive processing, errors, progress, and isDirty state, plus get(), post(), put(), patch(), and delete() methods.
What's Next
The upgrade itself isn't the hard part. The actual work is auditing your codebase for the three or four breaking changes that affect you specifically. Run through the checklist above item by item, fix what needs fixing, and test on a feature branch before touching main.
If you're building a new Laravel app from scratch and debating whether to use Inertia at all, v3 is the most compelling version yet. Smaller bundle, less configuration, and a feature set that competes seriously with decoupled SPA setups for most use cases.
Got a specific upgrade question or a weird edge case you ran into? Reach out, happy to dig into it.
Top comments (0)