Taylor Otwell dropped the announcement earlier today: Inertia 3 is in beta. If you've been building Laravel + Inertia apps for a while, this release is worth paying attention to. There's a lot here - a new Vite plugin, optimistic updates, instant navigation, layout props, a useHttp hook, simplified SSR, and Axios being replaced with a built-in XHR client. Let's go through it.
The @inertiajs/vite plugin is the headline change in terms of developer experience. It takes over page resolution, code splitting, and SSR setup automatically. Your entire entry point can now be a single function call:
1// vite.config.js 2export default defineConfig({ 3 plugins: [ 4 laravel({ 5 input: ['resources/js/app.js'], 6 refresh: true, 7 }), 8 inertia(), 9 vue(),10 ],11})12 13// resources/js/app.js14import { createInertiaApp } from '@inertiajs/vue3'15 16createInertiaApp()
That's it. No manual glob imports, no resolver config, no SSR entry file to maintain separately.
Speaking of SSR - development SSR no longer requires a separate Node.js server. With the Vite plugin, composer dev is all you need. No separate build step, no separate process, and error reporting is significantly better.
Optimistic updates are now a first-class feature. Chain .optimistic() before any router visit to apply prop changes immediately on the client. If the server request fails, the props revert automatically - no manual rollback logic needed:
1import { router } from '@inertiajs/vue3' 2 3router 4 .optimistic((props) => ({ 5 post: { 6 ...props.post, 7 is_favorite: !props.post.is_favorite, 8 }, 9 }))10 .post(`/posts/${post.id}/favorite`)
Clean, predictable, and you don't have to wire up your own state management just to make a toggle feel instant.
Instant visits let you swap to the target page component immediately while the server request fires in the background. The user sees the new page right away using shared props, and the full data merges in once the response arrives.
You can do this declaratively via the Link component:
1<template>2 <Link3 href="/features/navigation/target"4 component="Features/Navigation/Target"5 >6 Navigate Instantly7 </Link>8</template>
Or programmatically with placeholder props:
1// Or programmatically with placeholder props...2router.visit(url, {3 component: 'Features/Navigation/Target',4 pageProps: (currentProps, sharedProps) => ({5 ...sharedProps,6 greeting: 'Loading from server...',7 items: [],8 }),9})
useHttp HookThis is a useful one for apps that mix Inertia navigation with direct API calls. useHttp gives you the same developer experience as useForm, but for plain HTTP requests - reactive state, error handling, file upload progress, and request cancellation, all without triggering a page visit:
1<script setup> 2import { useHttp } from '@inertiajs/vue3' 3 4const http = useHttp({ name: '' }) 5 6function postToApi() { 7 http.post('/api/endpoint', { 8 onSuccess: (response) => { 9 console.log(response)10 },11 })12}13</script>14 15<template>16 <input v-model="form.name" />17 <button :disabled="form.processing" @click="postToApi">18 {{ form.processing ? 'Sending...' : 'Send Request' }}19 </button>20</template>
If you've ever hacked around Inertia's form helper to make API calls that don't need a full page visit, this is the proper solution.
The new useLayoutProps hook lets layouts declare default values that pages can override. Layouts consume useLayoutProps to get reactive state, and pages call setLayoutProps() to push their own values in:
1<!-- AppLayout.vue --> 2<script setup> 3import { useLayoutProps } from '@inertiajs/vue3' 4 5const layout = useLayoutProps({ 6 subtitle: '', 7}) 8</script> 9 10<template>11 <div v-if="layout.subtitle" class="banner">12 {{ layout.subtitle }}13 </div>14 <slot />15</template>
From any page:
1<!-- Any page -->2<script setup>3import { setLayoutProps, resetLayoutProps } from '@inertiajs/vue3'4 5setLayoutProps({ subtitle: 'Welcome back!' })6 7// Reset to defaults...8resetLayoutProps()9</script>
Nested layouts are supported too. This replaces the various workarounds people have been using - emitting events upward, using a store just for layout state, or reaching into $parent.
Custom error pages for 500s and other HTTP exceptions now have a proper API. The handleExceptionsUsing() method lets you render Inertia error pages with full access to shared data:
1// app/Providers/AppServiceProvider.php 2use Inertia\ExceptionResponse; 3use Inertia\Inertia; 4 5Inertia::handleExceptionsUsing( 6 function (ExceptionResponse $response) { 7 if (in_array($response->statusCode(), [ 8 403, 404, 419, 429, 500, 503 9 ])) {10 return $response11 ->render('ErrorPage', [12 'status' => $response->statusCode(),13 ])14 ->withSharedData();15 }16 }17);
The ->withSharedData() call is the key part - your error page gets the same auth state, flash messages, and other shared data as any other Inertia page.
Axios has been replaced with a built-in XHR client. Smaller bundle, fewer dependencies. Built-in HTTP interceptors cover the same extension points Axios adapters provided, and if you're heavily invested in Axios-specific behavior, there's an Axios adapter available to keep using it.
Beyond the headline features, v3 also ships: nested prop types with dot-notation support, a default layout option in createInertiaApp, TypeScript form component generics, enum support in Inertia::render(), and a preserveErrors option for partial reloads.
It's a beta, so production apps should wait. But if you have a test project or a staging environment, now is a good time to kick the tires and report issues. The Vite plugin alone is going to clean up a lot of boilerplate in existing codebases.
The official docs and upgrade guide should be available on inertiajs.com - keep an eye there for the stable release.
If you enjoyed this article, please consider supporting our work for as low as $5 / month.
Sponsor
Written by
Writing and maintaining @LaravelMagazine. Host of "The Laravel Magazine Podcast". Pronouns: vi/vim.
Get latest news, tutorials, community articles and podcast episodes delivered to your inbox.