gospa

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 25, 2026 License: Apache-2.0 Imports: 22 Imported by: 4

README

GoSPA

gospa1 128 gospa2 128

A Go framework for building reactive SPAs with server-side rendering. Brings Svelte-like reactivity to Go using Fiber and Templ.

Features

  • Reactive PrimitivesRune[T], Derived[T], Effect - Svelte-like reactivity in Go
  • File-Based Routing — SvelteKit-style routing for .templ files
  • WebSocket Sync — Real-time client-server state synchronization
  • Session Management — Secure session persistence with SessionStore and ClientStateStore
  • Type Safety — Compile-time template validation with Templ
  • Lightweight Runtime — ~11KB for the simple runtime, ~17KB for the full runtime with DOMPurify.
  • Remote Actions — Type-safe server functions callable directly from the client.
  • Security — Built-in CSRF protection, customizable CORS origins, and strict XSS prevention.
  • Rendering Modes — Seamlessly mix CSR, SSR, and SSG per-page rendering strategies.

Installation

go get github.com/aydenstechdungeon/gospa

Quick Start

1. Initialize Project
go run github.com/aydenstechdungeon/gospa/cmd/gospa@latest create myapp

or from examples/:

go run ../cmd/gospa create myapp

from inside a examples project:

go run ../../cmd/gospa create myapp
mkdir myapp && cd myapp
go mod init myapp
2. Create Main File
// main.go
package main

import (
    "log"
    _ "myapp/routes" // Import routes to trigger init()
    
    "github.com/aydenstechdungeon/gospa"
)

func main() {
    app := gospa.New(gospa.Config{
        RoutesDir: "./routes",
        DevMode:   true,
        AppName:   "myapp",
    })

    if err := app.Run(":3000"); err != nil {
        log.Fatal(err)
    }
}
3. Create a Page
// routes/page.templ
package routes

templ Page() {
    <div data-gospa-component="counter" data-gospa-state='{"count":0}'>
        <h1>Counter</h1>
        <span data-bind="text:count">0</span>
        <button data-on="click:increment">+</button>
        <button data-on="click:decrement">−</button>
    </div>
}
4. Run
go run main.go

Core Concepts

State Modes

GoSPA supports two state management modes:

Local State (Client-Only)

State lives entirely in the browser. No server synchronization.

<div data-gospa-component="counter" data-gospa-local>
    <span data-bind="text:count">0</span>
    <button data-on="click:increment">+</button>
</div>
Synced State (Client-Server)

State synchronizes across all connected clients via WebSocket.

// Server-side handler with broadcast
fiber.RegisterActionHandler("increment", func(client *fiber.WSClient, payload json.RawMessage) {
    GlobalCounter.Count++
    fiber.BroadcastState(hub, "count", GlobalCounter.Count)
})
Reactive Primitives
Rune[T] — Reactive State
count := state.NewRune(0)
count.Get()           // 0
count.Set(5)          // notifies subscribers
count.Update(func(v int) int { return v + 1 })
Derived[T] — Computed State
count := state.NewRune(5)
doubled := state.NewDerived(func() int {
    return count.Get() * 2
})
doubled.Get() // 10
Effect — Side Effects
cleanup := state.NewEffect(func() func() {
    fmt.Println("Count:", count.Get())
    return func() { fmt.Println("cleanup") }
})
defer cleanup()
File-Based Routing
routes/
├ root_layout.templ    → Base HTML shell
├ page.templ           → /
├ about/
│   └ page.templ       → /about
├ blog/
│   ├── layout.templ   → Layout for /blog/*
│   └ [id]/
│       └ page.templ   → /blog/:id
└ posts/
    └ [...rest]/
        └ page.templ   → /posts/* (catch-all)
Embedded Routes (Production)

For production, you can embed your routes into the binary using go:embed. This allows for a zero-dependency, single-binary distribution.

// prod.go
//go:embed routes/*
var embeddedRoutes embed.FS

// main.go
var routesFS fs.FS
if devMode {
    routesFS = os.DirFS("./routes") // Real files for hot-reloading
} else {
    // Use the embedded files for production
    sub, _ := fs.Sub(embeddedRoutes, "routes")
    routesFS = sub
}

app := gospa.New(gospa.Config{
    RoutesFS: routesFS,
    DevMode:  devMode,
    // ...
})
Client Runtime
// Initialize component
GoSPA.init({ wsUrl: 'ws://localhost:3000/_gospa/ws' })

// Reactive state
const count = new GoSPA.Rune(0)
const doubled = new GoSPA.Derived(() => count.get() * 2)

// Effects
new GoSPA.Effect(() => {
    console.log('Count:', count.get())
})

// DOM binding
GoSPA.bindElement('element-id', count)

// Navigation
GoSPA.navigate('/about')
GoSPA.prefetch('/blog')

// Transitions
GoSPA.fade(element, { duration: 300 })
Performance vs Security (Simple Runtime)

By default, the client runtime includes DOMPurify for robust XSS protection on dynamically bound templates. If you prefer a smaller bundle size and are comfortable with a less strictly-secured basic sanitizer, you can switch to the lightweight runtime using the configuration flag:

app := gospa.New(gospa.Config{
    // ...
    SimpleRuntime: true, 
})
Remote Actions

Remote Actions allow you to Define type-safe server functions that can be invoked seamlessly from the client without manually managing HTTP endpoints.

// Register on server
routing.RegisterRemoteAction("saveData", func(ctx context.Context, input interface{}) (interface{}, error) {
    // Process data securely on the server
    return map[string]string{"status": "success"}, nil
})

// Configure endpoint restrictions
app := gospa.New(gospa.Config{
    RemotePrefix:       "/api/rpc",
    MaxRequestBodySize: 1024 * 1024, // Limit body to 1MB
})
// Call from client
const result = await GoSPA.callAction('saveData', { id: 123 });
Application Security

GoSPA comes with secure defaults, but robust configurations exist for production use to secure cross-origin requests and mitigate CSRF attacks:

app := gospa.New(gospa.Config{
    // ...
    EnableCSRF: true,
    AllowedOrigins: []string{"https://myapp.com", "https://api.myapp.com"},
})
Partial Hydration

Opt out of reactivity for static content:

<div data-gospa-static>
    <h1>Static content — no bindings or event listeners</h1>
</div>
Transitions
<div data-transition="fade" data-transition-params='{"duration": 300}'>
    Fades in and out
</div>

<div data-transition-in="fly" data-transition-out="slide">
    Different enter/exit animations
</div>

Project Structure

myapp/
├ routes/              # Auto-routed .templ files
│   ├── page.templ
│   └ about/
│       └ page.templ
├ components/          # Reusable .templ components
├ lib/                 # Shared Go code
│   └ state.go         # App state
├ main.go
└ go.mod

API Reference

See docs/API.md for complete API documentation.

CLI

gospa create myapp    # Create new project
gospa generate        # Generate types and routes
gospa dev             # Development server with hot reload
gospa build           # Production build

Run any command with --help (e.g., gospa build --help) to see all available options and flags.

For more details, see the CLI Reference.

Plugin Ecosystem

GoSPA includes a powerful plugin system for extending build and development workflows.

Built-in Plugins
Plugin Description Commands
Tailwind CSS processing with Tailwind CSS v4 gospa add:tailwind (alias: at), gospa tailwind:build (alias: tb), gospa tailwind:watch (alias: tw)
PostCSS Advanced CSS with plugins (autoprefixer, typography, forms) gospa add:postcss (alias: ap), gospa postcss:build (alias: pb), gospa postcss:watch (alias: pw), gospa postcss:config (alias: pc)
Image Image optimization and responsive variants gospa image:optimize (alias: io), gospa image:clean (alias: ic), gospa image:sizes (alias: is)
Validation Form validation (Valibot client + Go validator server) gospa validation:generate (alias: vg), gospa validation:create (alias: vc), gospa validation:list (alias: vl)
SEO Sitemap, robots.txt, meta tags, structured data gospa seo:generate (alias: sg), gospa seo:meta (alias: sm), gospa seo:structured (alias: ss)
Auth OAuth2, JWT sessions, TOTP/OTP authentication gospa auth:generate (alias: ag), gospa auth:secret (alias: as), gospa auth:otp (alias: ao), gospa auth:backup (alias: ab), gospa auth:verify (alias: av)
QRCode QR code generation with customizable options Programmatic API only (no CLI commands)
Configuration

Plugins are configured in gospa.yaml:

plugins:
  tailwind:
    input: ./styles/main.css
    output: ./static/css/output.css
  image:
    input: ./images
    output: ./static/images
    formats: [webp, jpeg]
    sizes: [320, 640, 1280, 1920]
  auth:
    jwt_secret: ${JWT_SECRET}
    oauth:
      google:
        client_id: ${GOOGLE_CLIENT_ID}
        client_secret: ${GOOGLE_CLIENT_SECRET}
Plugin Hooks

Plugins integrate at key lifecycle points:

  • BeforeGenerate / AfterGenerate — Code generation
  • BeforeDev / AfterDev — Development server
  • BeforeBuild / AfterBuild — Production build
Creating Custom Plugins
package myplugin

import "github.com/aydenstechdungeon/gospa/plugin"

type MyPlugin struct{}

func (p *MyPlugin) Name() string { return "my-plugin" }
func (p *MyPlugin) Init() error { return nil }
func (p *MyPlugin) Dependencies() []plugin.Dependency {
    return []plugin.Dependency{
        {Name: "some-go-package", Type: plugin.DepGo},
        {Name: "some-bun-package", Type: plugin.DepBun},
    }
}
func (p *MyPlugin) OnHook(hook plugin.Hook, ctx map[string]interface{}) error {
    // Handle lifecycle hooks
    return nil
}
func (p *MyPlugin) Commands() []plugin.Command {
    return []plugin.Command{
        {Name: "my-plugin:run", Short: "mp", Description: "Run my plugin"},
    }
}

See docs/PLUGINS.md for complete plugin documentation.

Architecture

┌─────────────────────────────────────────────────────────────┐
│                         Browser                              │
│  ┌─────────────────────────────────────────────────────┐    │
│  │              GoSPA Runtime (<15KB*)                   │    │
│  │  ┌─────────┐ ┌──────────┐ ┌─────────┐ ┌──────────┐ │    │
│  │  │  Rune   │ │ Derived  │ │ Effect  │ │WebSocket │ │    │
│  │  └────┬────┘ └────┬─────┘ └────┬────┘ └────┬─────┘ │    │
│  │       └───────────┴────────────┴──────────┘        │    │
│  │                      │                              │    │
│  │              ┌───────┴───────┐                     │    │
│  │              │  DOM Binder   │                     │    │
│  │              └───────────────┘                     │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
                               │
                               │ WebSocket / HTTP
                               ▼
┌─────────────────────────────────────────────────────────────┐
│                       Go Server                              │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                    Fiber App                         │    │
│  │  ┌──────────────┐  ┌──────────────┐                 │    │
│  │  │   Runtime    │  │   WebSocket  │                 │    │
│  │  │  Middleware  │  │   Handler    │                 │    │
│  │  └──────────────┘  └──────────────┘                 │    │
│  └─────────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                  State Package                       │    │
│  │  ┌─────────┐ ┌──────────┐ ┌─────────┐ ┌──────────┐ │    │
│  │  │  Rune   │ │ Derived  │ │ Effect  │ │  Batch   │ │    │
│  │  └─────────┘ └──────────┘ └─────────┘ └──────────┘ │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

Comparison

Feature GoSPA HTMX Alpine SvelteKit
Language Go HTML JS JS/TS
Runtime Size <15KB* ~14KB ~15KB Varies
SSR
Type Safety
WebSocket
File Routing
Reactivity

License

Apache License 2.0

Documentation

Overview

Package gospa provides a modern SPA framework for Go with Fiber and Templ. It brings Svelte-like reactivity and state management to Go.

Index

Constants

View Source
const Version = "0.1.0"

Version is the current version of GoSPA.

Variables

This section is empty.

Functions

This section is empty.

Types

type App

type App struct {
	// Config is the application configuration.
	Config Config
	// Router is the file-based router.
	Router *routing.Router
	// Fiber is the underlying Fiber app.
	Fiber *fiberpkg.App
	// Hub is the WebSocket hub for real-time updates.
	Hub *fiber.WSHub
	// StateMap is the global state map.
	StateMap *state.StateMap
	// contains filtered or unexported fields
}

App is the main GoSPA application.

func New

func New(config Config) *App

New creates a new GoSPA application.

func (*App) Broadcast

func (a *App) Broadcast(message []byte)

Broadcast sends a message to all connected WebSocket clients.

func (*App) BroadcastState

func (a *App) BroadcastState(key string, value interface{}) error

BroadcastState broadcasts a state update to all connected WebSocket clients.

func (*App) Delete

func (a *App) Delete(path string, handlers ...fiberpkg.Handler)

Delete adds a DELETE route.

func (*App) Get

func (a *App) Get(path string, handlers ...fiberpkg.Handler)

Get adds a GET route.

func (*App) GetFiber

func (a *App) GetFiber() *fiberpkg.App

GetFiber returns the underlying Fiber app.

func (*App) GetHub

func (a *App) GetHub() *fiber.WSHub

GetHub returns the WebSocket hub.

func (*App) GetRouter

func (a *App) GetRouter() *routing.Router

GetRouter returns the file-based router.

func (*App) Group

func (a *App) Group(prefix string, handlers ...fiberpkg.Handler) fiberpkg.Router

Group creates a route group.

func (*App) Post

func (a *App) Post(path string, handlers ...fiberpkg.Handler)

Post adds a POST route.

func (*App) Put

func (a *App) Put(path string, handlers ...fiberpkg.Handler)

Put adds a PUT route.

func (*App) RegisterRoutes

func (a *App) RegisterRoutes() error

RegisterRoutes registers all scanned routes with Fiber.

func (*App) Run

func (a *App) Run(addr string) error

Run starts the application on the given address.

func (*App) RunTLS

func (a *App) RunTLS(addr, certFile, keyFile string) error

RunTLS starts the application with TLS on the given address.

func (*App) Scan

func (a *App) Scan() error

Scan scans the routes directory and builds the route tree.

func (*App) Shutdown

func (a *App) Shutdown() error

Shutdown gracefully shuts down the application.

func (*App) Static

func (a *App) Static(prefix, root string)

Static serves static files.

func (*App) Use

func (a *App) Use(middleware ...fiberpkg.Handler)

Use adds a middleware to the application.

type Config

type Config struct {
	// RoutesDir is the directory containing route files.
	RoutesDir string
	// RoutesFS is the filesystem containing route files (optional). Takes precedence over RoutesDir if provided.
	RoutesFS fs.FS
	// DevMode enables development features.
	DevMode bool
	// RuntimeScript is the path to the client runtime script.
	RuntimeScript string
	// StaticDir is the directory for static files.
	StaticDir string
	// StaticPrefix is the URL prefix for static files.
	StaticPrefix string
	// AppName is the application name.
	AppName string
	// DefaultState is the initial state for new sessions.
	DefaultState map[string]interface{}
	// EnableWebSocket enables WebSocket support.
	EnableWebSocket bool
	// WebSocketPath is the WebSocket endpoint path.
	WebSocketPath string
	// WebSocketMiddleware allows injecting session/auth middleware before WebSocket upgrade.
	WebSocketMiddleware fiberpkg.Handler

	// New Performance Options
	CompressState     bool // Compress WebSocket messages
	StateDiffing      bool // Only send state diffs
	CacheTemplates    bool // Cache compiled templates
	SimpleRuntime     bool // Use lightweight runtime without DOMPurify
	SimpleRuntimeSVGs bool // Allow SVG elements in simple runtime (security risk if content is untrusted)

	// New WebSocket Options
	WSReconnectDelay time.Duration // Initial reconnect delay
	WSMaxReconnect   int           // Max reconnect attempts
	WSHeartbeat      time.Duration // Heartbeat interval

	// New Hydration Options
	HydrationMode    string // "immediate" | "lazy" | "visible"
	HydrationTimeout int    // ms before force hydrate

	// New Serialization Options
	StateSerializer   StateSerializerFunc
	StateDeserializer StateDeserializerFunc

	// Routing Options
	DisableSPA bool // Disable SPA navigation completely
	SSR        bool // Global SSR mode

	// Remote Action Options
	MaxRequestBodySize int    // Maximum allowed size for remote action request bodies
	RemotePrefix       string // Prefix for remote action endpoints (default "/_gospa/remote")

	// Security Options
	AllowedOrigins []string // Allowed CORS origins
	EnableCSRF     bool     // Enable automatic CSRF protection
}

Config holds the application configuration.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns the default configuration.

type StateDeserializerFunc

type StateDeserializerFunc func([]byte, interface{}) error

StateDeserializerFunc defines a function for state deserialization

type StateSerializerFunc

type StateSerializerFunc func(interface{}) ([]byte, error)

StateSerializerFunc defines a function for state serialization

Directories

Path Synopsis
Package cli provides command-line interface tools for GoSPA.
Package cli provides command-line interface tools for GoSPA.
cmd
gospa command
Package main provides the gospa CLI tool.
Package main provides the gospa CLI tool.
gospa-gen command
Package main provides a code generator for GoSPA route registration.
Package main provides a code generator for GoSPA route registration.
Package component provides island architecture support for GoSPA.
Package component provides island architecture support for GoSPA.
starter
Package starter provides a library of reusable UI components for GoSPA applications
Package starter provides a library of reusable UI components for GoSPA applications
Package embed provides embedded static assets for the GoSPA framework.
Package embed provides embedded static assets for the GoSPA framework.
examples
form-remote module
Package fiber provides error overlay functionality for development.
Package fiber provides error overlay functionality for development.
auth
Package auth provides authentication for GoSPA projects.
Package auth provides authentication for GoSPA projects.
image
Package image provides image optimization for GoSPA projects.
Package image provides image optimization for GoSPA projects.
postcss
Package postcss provides a PostCSS plugin for GoSPA with Tailwind CSS v4 support.
Package postcss provides a PostCSS plugin for GoSPA with Tailwind CSS v4 support.
qrcode
Package qrcode provides QR code generation for GoSPA applications.
Package qrcode provides QR code generation for GoSPA applications.
seo
Package seo provides SEO optimization for GoSPA projects.
Package seo provides SEO optimization for GoSPA projects.
tailwind
Package tailwind provides a Tailwind CSS v4 plugin for GoSPA.
Package tailwind provides a Tailwind CSS v4 plugin for GoSPA.
validation
Package validation provides form validation for GoSPA projects.
Package validation provides form validation for GoSPA projects.
Package routing provides file-based routing similar to SvelteKit.
Package routing provides file-based routing similar to SvelteKit.
generator
Package generator provides code generation for automatic route registration.
Package generator provides code generation for automatic route registration.
Package state provides batch update support for reactive primitives.
Package state provides batch update support for reactive primitives.
Package templ provides Templ integration helpers for GoSPA reactive bindings.
Package templ provides Templ integration helpers for GoSPA reactive bindings.
website module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL