Installation
Server
composer require fydellys/inertia-static
php artisan vendor:publish --tag=inertia-static-configThe service provider, its middleware and the StaticCache facade are registered automatically.
For CDNs without tag purging (nginx, CloudFront, Bunny), enable a tag registry so purges by tag become URL purges:
php artisan vendor:publish --tag=inertia-static-migrations
php artisan migrateINERTIA_STATIC_REGISTRY=database # or "cache" (e.g. Redis)Client
The built client ships inside the Composer package (vendor/fydellys/inertia-static/js), like Ziggy. There is no npm package to install.
// vite.config.js
import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'
import vue from '@vitejs/plugin-vue'
import inertiaStatic from './vendor/fydellys/inertia-static/js/vite.js'
export default defineConfig({
plugins: [laravel({ /* ... */ }), vue(), inertiaStatic()],
})The plugin:
- makes
@fydellys/inertia-static/vueand/reactimportable, pointing to the client of the Inertia version you have installed (2 or 3); - dedupes
@inertiajs/core,vue,react,react-domandaxios, so the client and your app share one copy; - writes
resources/js/inertia-static.json(php artisan inertia-static:manifest) when the build starts.
| Option | Default | |
|---|---|---|
manifest | true | Set false to skip writing the manifest |
php | 'php' | PHP binary |
artisan | 'artisan' | Path to artisan |
inertia | detected | 2 or 3: the Inertia version, when it cannot be detected from node_modules |
Then add the client to createInertiaApp. The code is the same on Inertia 2 and 3:
// resources/js/app.ts
import { createInertiaApp } from '@inertiajs/vue3'
import { withInertiaStatic } from '@fydellys/inertia-static/vue'
import manifest from './inertia-static.json'
createInertiaApp({
...withInertiaStatic({ manifest }),
// resolve, setup, ...
})// resources/js/app.tsx
import { createInertiaApp } from '@inertiajs/react'
import { withInertiaStatic } from '@fydellys/inertia-static/react'
import manifest from './inertia-static.json'
createInertiaApp({
...withInertiaStatic({ manifest }),
// resolve, setup, ...
})useStaticIsland, <StaticIsland> and ensureCsrfCookie come from the same import.
Vue with SSR: wrap renderToString with ssrRender, created inside the createServer callback. On Inertia 3 it changes nothing; on Inertia 2 it is required (Vue 3.5.44+ would render [object Promise]):
// resources/js/ssr.ts
import { createInertiaApp } from '@inertiajs/vue3'
import createServer from '@inertiajs/vue3/server'
import { renderToString } from 'vue/server-renderer'
import { ssrRender } from '@fydellys/inertia-static/vue'
createServer((page) => createInertiaApp({ page, render: ssrRender(renderToString), /* ... */ }))TypeScript: point the alias at the bundled types in tsconfig.json:
{
"compilerOptions": {
"paths": {
"@fydellys/inertia-static": ["./vendor/fydellys/inertia-static/js/dist/index.d.ts"],
"@fydellys/inertia-static/*": ["./vendor/fydellys/inertia-static/js/dist/*"]
}
}
}Without the Vite plugin, import the client of your Inertia version directly: inertiaStatic() (Inertia 3) or installInertiaStatic() (Inertia 2).
TIP
Without an imported manifest, the client fetches /__inertia/manifest.json once. If that fails, every visit keeps the normal Inertia flow (fail-open).
SSR
The package works with or without Inertia SSR. Pages are cached with or without SSR. Set INERTIA_STATIC_SSR_REQUIRED=true to never cache a page that rendered without SSR while SSR is enabled (SSR server down): it is sent with no-store instead.
SSR server down
With the default, a page rendered while the SSR server is down is cached without its server-rendered content until it expires or is purged: search engines and social previews see the page without it in that window. Supervise the SSR server, purge after it is back (php artisan inertia-static:purge --all), or set INERTIA_STATIC_SSR_REQUIRED=true.
Configure the CDN
Pick a driver in .env (cloudflare, fastly, bunny, cloudfront, varnish, nginx, multi or null, the default) and follow its page (Choosing a driver):
INERTIA_STATIC_CDN=cloudflare
INERTIA_STATIC_BASE_URL=https://example.com
CLOUDFLARE_ZONE_ID=... # zone ID and token are only needed to purge;
CLOUDFLARE_API_TOKEN=... # without them the driver runs in headers-only mode