Skip to content

Overview & Setup

@stratal/inertia is an Inertia.js v3 server-side adapter for Stratal. It lets you build server-driven single-page applications with React while keeping your routing, controllers, and data logic entirely on the server. There is no client-side router and no API layer to maintain: your Stratal controllers render React pages directly.

Install the adapter alongside the Inertia.js client libraries, React, and the Vite tooling:

Terminal window
npm install @stratal/inertia @inertiajs/react @inertiajs/vite react react-dom
npm install -D @types/react @types/react-dom vite @cloudflare/vite-plugin

The Inertia CLI generates the recommended directory structure for you:

Terminal window
npx quarry inertia:install

This creates the client entry point, the streaming SSR entry, the root HTML template, a sample page component, and an initial type-definitions file. It also wires InertiaModule.forRoot() (with SSR enabled) into your src/app.module.ts.

The generated layout:

  • Directorysrc/
    • Directoryinertia/
      • app.tsx Client entry
      • ssr.tsx Streaming SSR entry
      • root.html Root HTML template
      • inertia.d.ts Generated page/prop types
      • Directorypages/
        • Home.tsx
      • Directorypublic/

Register InertiaModule in your application module using forRoot():

import { Module } from 'stratal/module'
import { InertiaModule, CookieFlashStore } from '@stratal/inertia'
import rootView from './inertia/root.html?raw'
@Module({
imports: [
InertiaModule.forRoot({
rootView,
version: '1.0.0',
ssr: {
bundle: () => import('./inertia/ssr'),
},
flash: {
store: new CookieFlashStore({ secret: env.FLASH_SECRET }),
},
routes: true,
sharedData: {
appName: 'My App',
},
i18n: {
only: ['common', 'nav'],
},
}),
],
})
export class AppModule {}
OptionTypeDescription
rootViewstringRaw HTML template string containing placeholder tags
versionstringApp version string used for cache busting
ssrInertiaSsrOptionsStreaming SSR configuration (bundle, disabled)
flashInertiaFlashOptionsFlash message store ({ store })
sharedDataRecord<string, unknown>Global props shared with every page (static values or (ctx) => value resolvers)
i18nInertiaI18nOptionsShare backend translations with the frontend ({ only })
routesbooleanInject serialized named routes for client-side URL generation with useRoute()
seoInertiaSeoOptionsApp-wide SEO defaults and title template for backend-driven page metadata
entryClientPathstringPath to the client entry file (default: src/inertia/app.tsx)

When your Inertia configuration depends on other providers (for example, a config service), use forRootAsync():

import { Module } from 'stratal/module'
import { InertiaModule } from '@stratal/inertia'
import { ConfigService } from './config.service'
@Module({
imports: [
InertiaModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
rootView: config.get('INERTIA_ROOT_VIEW'),
version: config.get('APP_VERSION'),
ssr: { bundle: () => import('./inertia/ssr') },
routes: true,
}),
}),
],
})
export class AppModule {}

The rootView option expects a raw HTML string with special placeholder tags. Import src/inertia/root.html with the ?raw suffix so Vite inlines its contents as a string:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
@viteHead
@inertiaHead
</head>
<body>
@inertia
@viteScripts
</body>
</html>
PlaceholderDescription
@inertiaReplaced with the serialized page data and the root #app element
@inertiaHeadReplaced with any <Head> tags and SEO metadata for the current page
@viteHeadReplaced with Vite CSS and preload links from the build manifest
@viteScriptsReplaced with Vite JavaScript <script> tags