Inertia.js integration
Inertia.js doesn’t require any integration configuration out of the box, however there are a few things to keep in mind.
Cross-domain redirects
Section titled “Cross-domain redirects”Redirects across domains (often using e.g. tenancy()->impersonate())
require that you use Inertia::location(). That way, a “regular” window.location = ... redirect
is performed, rather than a fetch() that may get blocked (you’d see CORS errors in the dev console in such
a case).
// Instead of:redirect(tenant_route('foo.acme.com', 'home'));// Use:Inertia::location(tenant_route('foo.acme.com', 'home'));Frontend route generation
Section titled “Frontend route generation”Depending on your frontend setup, you may be using Ziggy or Wayfinder to generate routes. These should
generally work out of the box in a multi-database setup. In a setup with path identification, i.e. where
routes have {tenant} parameters, you may need to ensure the parameter is being passed to the routes.
The basic approach is to simply do this manually — share the tenant ID (or preferably a slug so you don’t needlessly expose the tenant ID to the frontend) — with the frontend, as a global JavaScript variable in a Blade template, and then simply reference that variable when generating routes.
If you set URL::defaults(), Ziggy will generally respect that and you will not need to do the above.
You can either do that manually:
URL::defaults(['tenant' => tenant()->id]);// or if you use {tenant:slug} parameters:URL::defaults(['tenant:slug' => tenant()->slug]);or use the UrlGeneratorBootstrapper which automatically sets URL defaults (and optionally other things —
see the relevant documentation and class docblocks).
One additional use of the UrlGeneratorBootstrapper is that it can prefix route names (rather than just
setting parameters) depending on the current context. This can be useful if you’re integrating with packages
or starter kits specifically with route cloning. In other words, you have duplicated versions of, at least
some, routes as in:
- home
- tenant.home
- posts.index
- tenant.posts.index
- …
In such cases, if views or controllers are being shared, it would be impractical to have to modify each
route() call to be conditional like this:
tenant() ? route('tenant.foo', tenant()) : route('foo');The URL generator can prefix route names for you automatically if you’re in the tenant context.
This however only applies to routes generated server-side, not in JavaScript using tools like Ziggy.
Instead, you’d need to use an override like this:
@routes<script>window.tenancyInitialized = {{ tenant() ? 1 : 0 }}
if (window.tenancyInitialized) { const oldRoute = window.route window.route = function (name, params, absolute, config) { if (name && ! name.startsWith('tenant.')) { name = 'tenant.' + name }
return oldRoute(name, params, absolute, config) }}</script>@vite(['resources/js/app.js', "resources/js/Pages/{$page['component']}.vue"])That change will affect all route() calls that resolve to the global window.route helper (both
route() calls in plain JavaScript and route() calls in the <script> tags of your Vue components).
It does not affect route() calls in the <template> tags of your components — those go through
the ZiggyVue plugin (see the additional override below).
The code example exposes window.tenancyInitialized to the frontend, and when tenancy is initialized,
replaces the default window.route helper provided by Ziggy’s @routes Blade directive with one
that automatically prefixes the passed route name with tenant.. You can also use window.tenancyInitialized
in your own frontend code whenever you need to check whether tenancy is initialized.
To handle route() calls in <template> tags, replace the stock ZiggyVue plugin with a custom
one that delegates to window.route:
import { createApp, h } from 'vue';import { ZiggyVue } from '../../vendor/tightenco/ziggy';import { createInertiaApp } from '@inertiajs/vue3';import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';// ...
// Custom ZiggyVue plugin. Like the stock ZiggyVue plugin, it makes route()// available in the <template> tags of your Vue components. The reason we don't// just import and use ZiggyVue is that we want route() calls to go through the// window.route helper overridden in resources/views/app.blade.php.const ZiggyVue = { install(app, options) { // Delegates to window.route const r = (name, params, absolute, config = options) => route(name, params, absolute, config);
if (parseInt(app.version) > 2) { app.config.globalProperties.route = r; app.provide('route', r); } else { app.mixin({ methods: { route: r, }, }); } }};
createInertiaApp({ // ... setup({ el, App, props, plugin }) { return createApp({ render: () => h(App, props) }) .use(plugin) .use(ZiggyVue) .mount(el); }, // ...});This override makes route() available in the <template> tags of your Vue components, just like
the stock ZiggyVue plugin, but instead of resolving routes through Ziggy’s own helper (which would
bypass the window.route override above), it delegates to window.route. That way the single
override in resources/views/app.blade.php applies to route() calls in both the <script> and
<template> tags of your components.
Make sure you have a good reason for adding these overrides to your app. As mentioned above, this is
primarily useful when integrating with packages or adding multi-tenancy to an existing codebase where
replacing all existing route() calls would be impractical or impossible. It is preferable to simply
use the default route() helper if possible though since this override is a somewhat hacky
solution and it’s generally better if route() calls clearly communicate that they’re generating
tenant routes.