bifrost

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Feb 16, 2026 License: MIT Imports: 11 Imported by: 0

README

Bifrost

Server-side rendering for React components in Go. Bridge your Go backend with React frontends.

Installation

go get github.com/3-lines-studio/bifrost

Requires Bun to be installed for development and building. Production binaries with SSR pages include the Bun runtime and do not require Bun to be installed on the target system. Static-only apps (no SSR) do not include the Bun runtime.

Features

  • SSR - Server-side render React components
  • Hot Reload - Auto-refresh in development
  • Props Loading - Pass data from Go to React via typed options
  • File-based Routing - Simple page organization
  • Production Ready - Strict embedded assets for deployment
  • Self-Contained - Single binary with embedded Bun runtime only when SSR pages are used
  • Static Site Generation - Build static sites without runtime Bun dependency

Development vs Production

Bifrost has two distinct modes with strict separation:

Development (BIFROST_DEV=1):

  • Hot reload on file changes
  • Source TSX files rendered directly
  • Assets served from disk
  • Requires Bun installed on system

Production (absence of BIFROST_DEV):

  • Requires embedded assets via WithAssetsFS()
  • Requires pre-built artifacts from bifrost-build
  • Manifest-driven asset resolution
  • Uses embedded Bun runtime for SSR pages (no system Bun required)
  • Static-only apps do not include the Bun runtime
  • Strict fail-fast on missing assets or runtime for SSR pages

Quick Start

Copy and paste this command to create a new project:

go run github.com/3-lines-studio/bifrost/cmd/init@latest myapp

This creates a complete Bifrost project with a working SSR page and starts the dev server on http://localhost:8080.

Use another template:

  • go run github.com/3-lines-studio/bifrost/cmd/init@latest --template spa myapp
  • go run github.com/3-lines-studio/bifrost/cmd/init@latest --template desktop myapp
Build for Production
# Build assets and SSR bundles
bifrost-build main.go

# Build Go binary with embedded assets
go build -o myapp main.go

# Run in production (no BIFROST_DEV set)
./myapp
Repair Existing Project

If your .bifrost directory is missing or corrupted:

go run github.com/3-lines-studio/bifrost/cmd/doctor@latest .

doctor only repairs the .bifrost directory (does not scaffold missing app files).

API

Creating Pages
package main

import (
    "embed"
    "log"
    "net/http"
    
    "github.com/3-lines-studio/bifrost"
)

//go:embed all:.bifrost
var bifrostFS embed.FS

func main() {
    // Production: requires WithAssetsFS
    // Development: works without it
    r, err := bifrost.New(bifrost.WithAssetsFS(bifrostFS))
    if err != nil {
        log.Fatal(err)
    }
    defer r.Stop()
    
    // SSR page with props loader
    home := r.NewPage("./pages/home.tsx", 
        bifrost.WithPropsLoader(func(req *http.Request) (map[string]any, error) {
            return map[string]any{
                "name": "World",
            }, nil
        }),
    )
    
    // Static page (client-side only, no SSR)
    about := r.NewPage("./pages/about.tsx", 
        bifrost.WithClientOnly(),
    )
    
    // Static prerender page (full HTML at build time + hydration)
    blog := r.NewPage("./pages/blog.tsx",
        bifrost.WithStaticPrerender(),
    )
    
    // Setup routes
    router := http.NewServeMux()
    router.Handle("/", home)
    router.Handle("/about", about)
    
    // Register asset routes
    assetRouter := http.NewServeMux()
    bifrost.RegisterAssetRoutes(assetRouter, r, router)
    
    log.Fatal(http.ListenAndServe(":8080", assetRouter))
}
Page Options
  • WithPropsLoader(fn) - Provide a function to load props from the HTTP request
  • WithClientOnly() - Create a static page with client-side hydration only (empty shell)
  • WithStaticPrerender() - Prerender full HTML at build time + client hydration
Mode Detection

Bifrost determines mode by checking the BIFROST_DEV environment variable:

  • BIFROST_DEV=1 → Development mode
  • Any other value or unset → Production mode

In production mode:

  • WithAssetsFS() is required and validated at startup
  • SSR bundles are extracted from embedded assets
  • Source TSX files are not used
  • Missing assets cause immediate errors

Static Site Generation

Build static sites that don't require Bun at runtime:

// Client-only page (empty shell + client render)
home := r.NewPage("./pages/home.tsx", bifrost.WithClientOnly())

// Static prerender page (full HTML at build time + hydration)
blog := r.NewPage("./pages/blog.tsx", bifrost.WithStaticPrerender())

// SSR page (server-side render on each request)
dynamic := r.NewPage("./pages/dynamic.tsx", 
    bifrost.WithPropsLoader(loader),
)

Build with:

bifrost-build main.go

This generates:

  • .bifrost/dist/ - Client JS/CSS bundles
  • .bifrost/ssr/ - Server bundles for SSR pages only
  • .bifrost/pages/ - Static HTML files:
    • Client-only: empty shell HTML
    • Static prerender: full HTML with rendered body
  • .bifrost/manifest.json - Asset manifest with mode info

Architecture

Bifrost is organized into focused internal packages:

  • internal/types - Shared types and contracts
  • internal/runtime - Bun process management and IPC
  • internal/page - HTTP handler orchestration
  • internal/assets - Manifest resolution and embedded FS
  • internal/build - Build pipeline and AST discovery
  • internal/cli - Terminal output helpers

Error Handling

Redirects

Return a redirect from props loader:

page := r.NewPage("./pages/protected.tsx", 
    bifrost.WithPropsLoader(func(req *http.Request) (map[string]any, error) {
        if !isAuthenticated(req) {
            return nil, &RedirectError{
                URL:    "/login",
                Status: http.StatusFound,
            }
        }
        // ...
    }),
)

Implement the RedirectError interface:

type RedirectError interface {
    RedirectURL() string
    RedirectStatusCode() int
}
Production Errors

Bifrost enforces strict production requirements:

  • ErrAssetsFSRequiredInProd - Returned when WithAssetsFS() is missing in production
  • ErrManifestMissingInAssetsFS - Returned when manifest.json is not found in embedded assets
  • ErrEmbeddedRuntimeNotFound - Returned when embedded Bun runtime is missing (run bifrost-build to generate it)
  • ErrEmbeddedRuntimeExtraction - Returned when extracting embedded runtime fails
  • ErrEmbeddedRuntimeStart - Returned when embedded runtime fails to start

These errors are returned from bifrost.New() to fail fast at startup.

License

MIT

Documentation

Index

Constants

View Source
const (
	ModeSSR             = types.ModeSSR
	ModeClientOnly      = types.ModeClientOnly
	ModeStaticPrerender = types.ModeStaticPrerender
)

Variables

This section is empty.

Functions

func RegisterAssetRoutes

func RegisterAssetRoutes(r Router, renderer *Renderer, appRouter http.Handler)

Types

type Option

type Option func(*Renderer)

func WithAssetsFS

func WithAssetsFS(fs embed.FS) Option

type PageMode

type PageMode = types.PageMode

type PageOption

type PageOption = types.PageOption

func WithClientOnly

func WithClientOnly() PageOption

func WithPropsLoader

func WithPropsLoader(loader types.PropsLoader) PageOption

func WithStaticDataLoader

func WithStaticDataLoader(loader types.StaticDataLoader) PageOption

func WithStaticPrerender

func WithStaticPrerender() PageOption

type RedirectError

type RedirectError = types.RedirectError

type Renderer

type Renderer struct {
	// contains filtered or unexported fields
}

func New

func New(opts ...Option) (*Renderer, error)

func (*Renderer) NewPage

func (r *Renderer) NewPage(componentPath string, opts ...PageOption) http.Handler

func (*Renderer) Render

func (r *Renderer) Render(componentPath string, props map[string]any) (types.RenderedPage, error)

func (*Renderer) Stop

func (r *Renderer) Stop() error

type Router

type Router interface {
	Handle(pattern string, handler http.Handler)
}

type StaticBuildExport

type StaticBuildExport struct {
	Version int                `json:"version"`
	Pages   []StaticPageExport `json:"pages"`
}

StaticBuildExport represents the export format for static build data

type StaticDataLoader

type StaticDataLoader = types.StaticDataLoader

type StaticPageExport

type StaticPageExport struct {
	ComponentPath string             `json:"componentPath"`
	Entries       []StaticPathExport `json:"entries"`
}

StaticPageExport represents a single page's static paths

type StaticPathData

type StaticPathData = types.StaticPathData

Export types for use with WithStaticDataLoader

type StaticPathExport

type StaticPathExport struct {
	Path  string         `json:"path"`
	Props map[string]any `json:"props"`
}

StaticPathExport represents a single path entry

Directories

Path Synopsis
cmd
build command
doctor command
init command
internal
cli

Jump to

Keyboard shortcuts

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