Inertia.js
Build modern SPAs with Vue, React, or Svelte without building a separate API. Inertia lets your Go backend render pages and pass props; the frontend handles routing and UI. Powered by github.com/petaki/inertia-go.
Installation
nimbus plugin install inertia
Or scaffold a full Inertia app with a frontend kit:
nimbus create my-app --kit=react
nimbus create my-app --kit=vue
nimbus create my-app --kit=svelte
Register the Plugin
import "github.com/CodeSyncr/nimbus/plugins/inertia"
app.Use(inertia.New(inertia.Config{
URL: "http://localhost:3333",
RootTemplate: "resources/views/inertia_layout.nimbus",
Version: "1",
}))
Config Options
| Field | Description |
|---|---|
URL | Application URL (used in root template for redirects) |
RootTemplate | Path to root HTML template |
Version | Asset version string for cache busting |
SSRURL | Node.js SSR server URL (optional, e.g. http://localhost:13714) |
TemplateFS | Custom fs.FS for the root template (optional) |
Rendering Pages
Use inertia.Render() in handlers. When the browser makes a standard request, it returns full HTML. When the Inertia client sends an XHR (with X-Inertia header), it returns JSON page data:
func ShowDashboard(c *http.Context) error {
return inertia.Render(c, "Dashboard/Index", map[string]any{
"stats": loadDashboardStats(),
"user": auth.UserFromContext(c.Ctx()),
})
}
The component name (Dashboard/Index) maps to inertia/pages/Dashboard/Index.tsx (or .vue / .svelte).
Shared Data
Global Sharing
Share data available to every Inertia response. Only for static data that never changes per-request:
inertia.Share("appName", "My App")
inertia.Share("version", "2.1.0")
Request-Scoped Sharing (ShareProp)
ShareProp() shares data for the current request only โ safe for concurrent requests. Use this for user-specific data:
// In auth middleware
func AuthMiddleware(next router.HandlerFunc) router.HandlerFunc {
return func(c *http.Context) error {
user := auth.UserFromContext(c.Ctx())
inertia.ShareProp(c, "auth", map[string]any{
"user": user,
})
// Share flash messages
if c.Session != nil {
inertia.ShareProp(c, "flash", map[string]any{
"success": c.Session.GetFlash("success"),
"error": c.Session.GetFlash("error"),
})
}
return next(c)
}
}
Shared props merge into every inertia.Render() call automatically. Props passed directly to Render() take precedence over shared props with the same key.
Root Template
The root template wraps your SPA. It must include a data-page div and load your frontend entry:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
{{ if .viteDev }}
<script type="module" src="http://localhost:5173/@vite/client"></script>
<script type="module" src="http://localhost:5173/src/main.js"></script>
{{ else }}
<link rel="stylesheet" href="/build/assets/app.css">
{{ end }}
</head>
<body>
<div id="app" data-page="{{ marshal .page }}"></div>
{{ if not .viteDev }}
<script src="/build/assets/app.js"></script>
{{ end }}
</body>
</html>
When VITE_DEV=1 (set automatically by nimbus serve for Inertia apps), the template loads from the Vite dev server for HMR. In production, it loads built assets from /build/. If no template file exists, an embedded default is used.
Frontend Setup
React + Vite
// inertia/app.tsx
import { createInertiaApp } from '@inertiajs/react'
import { createRoot } from 'react-dom/client'
createInertiaApp({
resolve: name => {
const pages = import.meta.glob('./pages/**/*.tsx', { eager: true })
return pages[`./pages/${name}.tsx`]
},
setup({ el, App, props }) {
createRoot(el).render(<App {...props} />)
},
})
Vue 3 + Vite
// inertia/app.js
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'
createInertiaApp({
resolve: name => {
const pages = import.meta.glob('./pages/**/*.vue', { eager: true })
return pages[`./pages/${name}.vue`]
},
setup({ el, App, props, plugin }) {
createApp({ render: () => h(App, props) })
.use(plugin)
.mount(el)
},
})
Svelte + Vite
// inertia/app.js
import { createInertiaApp } from '@inertiajs/svelte'
createInertiaApp({
resolve: name => {
const pages = import.meta.glob('./pages/**/*.svelte', { eager: true })
return pages[`./pages/${name}.svelte`]
},
setup({ el, App, props }) {
new App({ target: el, props })
},
})
Server-Side Rendering (SSR)
Enable SSR by setting the SSRURL config to your Node.js SSR server:
app.Use(inertia.New(inertia.Config{
URL: "http://localhost:3333",
RootTemplate: "resources/views/inertia_layout.nimbus",
Version: "1",
SSRURL: "http://localhost:13714",
}))
Set up a Node.js SSR server using @inertiajs/vue3/server (or the React/Svelte equivalent) that calls createServer() and renderToString().
Running the App
npm install
nimbus serve
Everything runs on one server. Vite dev server starts automatically in the background with HMR. Edit inertia/ files and see changes instantly.
Project Structure
project/
โโโ inertia/ # Frontend (React/Vue/Svelte)
โ โโโ app.tsx # Entry point
โ โโโ layouts/ # Shared layouts
โ โโโ pages/ # Page components
โ โโโ Dashboard/
โ โ โโโ Index.tsx
โ โโโ Users/
โ โโโ Index.tsx
โ โโโ Create.tsx
โโโ resources/views/
โ โโโ inertia_layout.nimbus # Root HTML template
โโโ public/build/ # Vite output (production)
โโโ ...
Plugin Capabilities
| Capability | Description |
|---|---|
HasMiddleware | Inertia protocol (version negotiation, XHR detection) |
HasConfig | Default configuration values |
HasViews | Embedded fallback root template |
HasBindings | Registers the Inertia manager in the IoC container |
See also: Inertia Setup Guide, HMR Configuration, Unpoly (server-rendered alternative).