Project Awesome project awesome

Server-side > Go

Package 307 stars GitHub

Inertia GO

Inertia.js Go Adapter

Build Status License: MIT

An Inertia.js server-side adapter for Go. Visit inertiajs.com to learn more.

Installation

Install the package using the go get command:

go get github.com/petaki/inertia-go

Usage

1. Create new instance

url := "http://inertia-app.test" // Application URL for redirect
rootTemplate := "./app.gohtml"   // Root template, see the example below
version := ""                    // Asset version

inertiaManager := inertia.New(url, rootTemplate, version)

Or create with embed.FS for root template:

import "embed"

//go:embed template
var templateFS embed.FS

// ...

inertiaManager := inertia.New(url, rootTemplate, version, templateFS)

2. Register the middleware

mux := http.NewServeMux()
mux.Handle("/", inertiaManager.Middleware(homeHandler))

3. Render in handlers

func homeHandler(w http.ResponseWriter, r *http.Request) {
    // ...

    err := inertiaManager.Render(w, r, "home/Index", nil)
    if err != nil {
        // Handle server error...
    }
}

Or render with props:

// ...

err := inertiaManager.Render(w, r, "home/Index", map[string]any{
    "total": 32,
})

//...

4. Server-side Rendering (Optional)

First, enable SSR with the url of the Node server:

inertiaManager.EnableSsrWithDefault() // http://127.0.0.1:13714/render

Or with custom url:

inertiaManager.EnableSsr("http://ssr-host:13714/render")

Or with the Vite dev server:

inertiaManager.EnableSsr("http://localhost:5173/__inertia_ssr")

You can also provide a custom *http.Client:

client := &http.Client{
    Timeout: 10 * time.Second,
}

inertiaManager.EnableSsr("http://ssr-host:13714/render", client)
inertiaManager.EnableSsrWithDefault(client)

For more information, please read the official Server-side Rendering documentation on inertiajs.com.

Page Props

Name Method(s) Evaluation Full Partial
Base Share, WithProp, Render Eager ✅ if requested
Optional WithOptionalProp Lazy ✅ if requested
Always WithAlwaysProp Lazy ✅ always
Deferred WithDeferredProp Lazy ❌ deferred ✅ if requested
Merge WithMergeProp Lazy ✅ if requested
Deep Merge WithDeepMergeProp Lazy ✅ if requested
Prepend WithPrependProp Lazy ✅ if requested
Scroll WithScrollProp ✅ metadata ✅ metadata
Once WithOnceProp, WithOnce Lazy ✅ if requested
Error WithErrorProp Eager ✅ always ✅ always
  • WithOnce can be combined with Deferred, Merge, Deep Merge, Prepend, and Optional props.
  • WithOnceProp and WithOnce props are excluded when listed in the X-Inertia-Except-Once-Props header.
  • WithScrollProp adds scroll metadata to the page response for infinite scroll support.
  • WithErrorProp errors are merged with any inline errors map passed to Render.

Page Settings

Name Method(s) Evaluation Full Partial
Flash WithFlash Eager
Clear History WithClearHistory Eager
Encrypt History WithEncryptHistory Eager
Preserve Fragment WithPreserveFragment Eager
  • WithClearHistory, WithEncryptHistory, and WithPreserveFragment are emitted only when set to true.

Examples

The following examples show how to use the package.

Share a function with root template (globally)

inertiaManager.ShareFunc("asset", assetFunc)
<script src="{{ asset "js/app.js" }}"></script>

Share data with root template (globally)

inertiaManager.ShareViewData("env", "production")
{{ if eq .env "production" }}
    ...
{{ end }}

Share data with root template (context based)

ctx := inertiaManager.WithViewData(r.Context(), "meta", meta)
r = r.WithContext(ctx)
<meta name="description" content="{{ .meta }}">

Share a prop (globally)

inertiaManager.Share("title", "Inertia App Title")

Share a prop (context based)

func authenticate(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // ...
        
        ctx := inertiaManager.WithProp(r.Context(), "authUserID", user.ID)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

Optional prop (context based)

ctx := inertiaManager.WithOptionalProp(r.Context(), "extra", func() any {
    return getExtra()
})
r = r.WithContext(ctx)

Always prop (context based)

ctx := inertiaManager.WithAlwaysProp(r.Context(), "notifications", func() any {
    return getNotifications()
})
r = r.WithContext(ctx)

Deferred prop (context based)

ctx := inertiaManager.WithDeferredProp(r.Context(), "comments", func() any {
    return getComments()
})
r = r.WithContext(ctx)

Deferred prop with group (context based)

ctx := inertiaManager.WithDeferredProp(r.Context(), "comments", func() any {
    return getComments()
}, "my-group")
r = r.WithContext(ctx)

Merge prop (context based)

ctx := inertiaManager.WithMergeProp(r.Context(), "results", func() any {
    return getResults()
})
r = r.WithContext(ctx)

Or with match on:

ctx := inertiaManager.WithMergeProp(r.Context(), "results", func() any {
    return getResults()
}, "id")
r = r.WithContext(ctx)

Or with multiple nested match on paths:

ctx := inertiaManager.WithMergeProp(r.Context(), "complexData", func() any {
    return getComplexData()
}, "users.data.id", "messages.uuid")
r = r.WithContext(ctx)

Deep merge prop (context based)

ctx := inertiaManager.WithDeepMergeProp(r.Context(), "settings", func() any {
    return getSettings()
})
r = r.WithContext(ctx)

Or with match on:

ctx := inertiaManager.WithDeepMergeProp(r.Context(), "settings", func() any {
    return getSettings()
}, "id")
r = r.WithContext(ctx)

Prepend prop (context based)

ctx := inertiaManager.WithPrependProp(r.Context(), "notifications", func() any {
    return getNotifications()
})
r = r.WithContext(ctx)

Or with match on:

ctx := inertiaManager.WithPrependProp(r.Context(), "notifications", func() any {
    return getNotifications()
}, "id")
r = r.WithContext(ctx)

Scroll prop (context based)

ctx := inertiaManager.WithScrollProp(r.Context(), "items", inertia.ScrollPageProp{
    PageName:    "page",
    CurrentPage: 1,
    NextPage:    2,
})
r = r.WithContext(ctx)

Once prop (context based)

ctx := inertiaManager.WithOnceProp(r.Context(), "plans", func() any {
    return getPlans()
})
r = r.WithContext(ctx)

Once modifier (context based)

ctx := inertiaManager.WithMergeProp(r.Context(), "activity", func() any {
    return getActivity()
})
ctx = inertiaManager.WithOnce(ctx, "activity")
r = r.WithContext(ctx)

Or with expiration:

expiresAt := time.Now().Add(24 * time.Hour).UnixMilli()
ctx := inertiaManager.WithDeferredProp(r.Context(), "permissions", func() any {
    return getPermissions()
})
ctx = inertiaManager.WithOnce(ctx, "permissions", inertia.OncePageProp{ExpiresAt: &expiresAt})
r = r.WithContext(ctx)

Error prop (context based)

ctx := inertiaManager.WithErrorProp(r.Context(), "email", "Invalid email")
r = r.WithContext(ctx)

Or with multiple fields:

ctx := inertiaManager.WithErrorProp(r.Context(), "email", "Invalid email")
ctx = inertiaManager.WithErrorProp(ctx, "password", "Too short")
r = r.WithContext(ctx)

Flash (context based)

ctx := inertiaManager.WithFlash(r.Context(), map[string]any{
    "success": "Item created successfully",
})
r = r.WithContext(ctx)

Clear history (context based)

ctx := inertiaManager.WithClearHistory(r.Context())
r = r.WithContext(ctx)

Encrypt history (context based)

ctx := inertiaManager.WithEncryptHistory(r.Context())
r = r.WithContext(ctx)

Preserve fragment (context based)

ctx := inertiaManager.WithPreserveFragment(r.Context())
r = r.WithContext(ctx)

Root template

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link href="css/app.css" rel="stylesheet">
        <link rel="icon" type="image/x-icon" href="favicon.ico">
    </head>
    <body>
        <script data-page="app" type="application/json">{{ marshal .page }}</script>
        <div id="app"></div>
        <script src="js/app.js"></script>
    </body>
</html>

Root template with Server-side Rendering (SSR)

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link href="css/app.css" rel="stylesheet">
        <link rel="icon" type="image/x-icon" href="favicon.ico">
        {{ if .ssr }}
            {{ raw .ssr.Head }}
        {{ end }}
    </head>
    <body>
        {{ if not .ssr }}
            <script data-page="app" type="application/json">{{ marshal .page }}</script>
            <div id="app"></div>
        {{ else }}
            {{ raw .ssr.Body }}
        {{ end }}
        <script src="js/app.js"></script>
    </body>
</html>

Vite Integration

For Vite integration, check out the Usage with Inertia section in the petaki/support-go package.

Example Apps

Satellite

Vite Vite / Vue3 Vue3

https://github.com/petaki/satellite

Homettp

Vite Vite / Vue3 Vue3

https://github.com/homettp/homettp

Waterkube

Vite Vite / Vue3 Vue3

https://github.com/waterkube/waterkube

Contributors

Reporting Issues

If you are facing a problem with this package or found any bug, please open an issue on GitHub.

License

The MIT License (MIT). Please see License File for more information.

Back to Inertia.js