Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Inertia Bundle Laravel Package

rompetomp/inertia-bundle

Inertia.js adapter for Laravel for building single-page apps with classic server-side routing and controllers. Ships the middleware, root view, and config so Vue, React, or Svelte pages plug into Laravel without a separate API, with asset versioning and shared props built in.

View on GitHub
Deep Wiki
Context7

Getting Started

First, a scoping note that saves an afternoon: rompetomp/inertia-bundle is the Symfony adapter for Inertia.js, not a Laravel package. If you're on Laravel, stop here and use inertiajs/inertia-laravel — the mental model transfers, but almost every integration detail (service registration, middleware wiring, asset versioning, root view config) is different.

Minimal steps on Symfony:

  1. composer require rompetomp/inertia-bundle
  2. Enable the bundle in config/bundles.php
  3. Register the bundle's Inertia middleware globally (or on the routes that render pages) in config/packages/framework.yaml / kernel
  4. Point the bundle at your root template (the one containing the mount element and script tags) via bundle config, and render the page object from that template with the Twig helper the bundle ships
  5. In a controller, return an Inertia response instead of a rendered Twig view
use Inertia\Inertia;

public function index(): \Symfony\Component\HttpFoundation\Response
{
    return Inertia::render('Blog/Index', 'app.html.twig', [
        'posts' => $postRepository->findLatest(),
    ]);
}

First use case to get working end-to-end: a single GET route that returns a component name plus one array of props, rendered into a root template that outputs the JSON page object. Everything else in Inertia (partial reloads, versioning, shared data) builds on that one response shape — verify it in the browser's view-source and in the JSON response to an X-Inertia: true request before touching anything else.

Where to look first: the bundle's middleware (it owns the 409/303/redirect behaviour), the Response class (it owns props, lazy props, version, and — as of v2.4.3 — the page URL), and the Twig extension that renders the root element.

Implementation Patterns

Controller responses. Inertia::render($component, $rootView, $props) returns a response object whose methods are chainable. Keep controllers thin: build props, return the response, let the middleware negotiate HTML vs JSON.

Shared data. Don't duplicate auth user, flash messages, and validation errors across every controller. Register an event subscriber / kernel request listener that calls Inertia::share([...]) once per request. This is the Symfony equivalent of Laravel's HandleInertiaRequests middleware — there is no artisan generator for it, you write it.

final class InertiaSharedDataSubscriber implements EventSubscriberInterface
{
    public function onKernelRequest(RequestEvent $event): void
    {
        Inertia::share([
            'auth'   => fn () => $this->currentUserPayload(),
            'flash'  => fn () => $this->pullFlashMessages(),
            'errors' => fn () => $this->validationErrors(),
        ]);
    }
}

Lazy props and partial reloads. Wrap expensive props in closures and mark them lazy/optional so partial reloads (only / except from the client) skip them. This is the single biggest performance lever in an Inertia app; a closure that isn't marked lazy still runs on every partial reload that includes its key, and a plain closure passed as a prop is evaluated eagerly in the bundle's resolution step.

Asset versioning. Set a version derived from your build manifest (entrypoints.json / manifest.json hash). On mismatch, Inertia responds 409 with X-Inertia-Location for GET and forces a full reload — this is the mechanism that prevents users running stale JS against new props, and it only works if the version actually changes on deploy.

Custom page URL (v2.4.3). The response now accepts an explicit URL for the page object sent to the client, instead of always echoing the request URI. Use it when the URL the client should record differs from the one the server sees: localized/translated routes, a page rendered from a non-canonical path, or behind a proxy where the request URI isn't the public URL.

return Inertia::render('Blog/Show', 'app.html.twig', ['post' => $post])
    ->url($router->generate('blog_show', ['slug' => $post->getSlug()]));

Check PR #50 for the exact method signature in your version; the key point is that this changes only the url field in the page payload — it does not change the actual HTTP response URL or the router's notion of the current route.

Testing. Feature-test the JSON contract: send X-Inertia: true plus X-Inertia-Version, assert 200, Content-Type: application/json, and the component/props keys. Assert the new url override explicitly if you depend on it, since it's client-visible state that's easy to regress silently.

Gotchas and Tips

  • Middleware ordering is the whole ballgame. The Inertia middleware must see every response, including redirects. PUT/PATCH/DELETE requests that return a redirect must be converted to 303 or the browser will replay the method against the redirect target — the classic "my delete form now 405s" bug.
  • Version mismatch is invisible by default. If your app hard-reloads on every navigation, log the client X-Inertia-Version against the server version before debugging anything else. Usually it's a manifest read at container compile time or a version that's constant across deploys.
  • PHP empty arrays serialize as []. A prop you intend as an object (e.g. errors when there are none, or a config map) becomes a JS array and blows up on property access. Cast to (object) or stdClass for anything the frontend treats as a dictionary.
  • The custom URL is history state, not routing. Inertia's client router pushes page.url into history. If you set a URL that no real route serves, back/forward will land on a 404 or a different page than the one the user saw. Use it to correct a URL that genuinely corresponds to that resource, not to cosmetic-ise it.
  • Don't read page.url server-side. Your controllers and generateUrl() calls still see the real request. The override exists to reconcile server and client views; it isn't a rewrite of the request context.
  • Flash messages need pulling, not peeking. Shared data closures should consume one-time flash data, otherwise messages reappear on every subsequent Inertia visit (including partial reloads that re-share props).
  • CSRF. Inertia requests are XHRs; Symfony's CSRF protection rejects them unless the token is pulled from a meta tag and sent as a header (or the route is exempted). This surfaces as a 419-equivalent after a deploy or session rotation, not during development.
  • Root view changes are per-response. Overriding the root template for one page is fine, but doing it inconsistently means your asset tags and mount element live in two places. Keep exactly one root template unless you have a hard requirement.
  • Extension points worth knowing: decorate the bundle's response factory if you need project-wide defaults (version, root view, shared props); add a kernel subscriber for shared data; add a Twig extension if you need custom helpers around the root element.
  • Upgrading from 2.4.2 to 2.4.3 is low risk: a typo fix and an additive response capability with no changed defaults, so existing responses keep echoing the request URI. No migration needed — but if you've hand-rolled a workaround (e.g. rewriting the URL in the payload inside a listener), remove it in favour of the first-class API so the two don't fight.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource