go-embedded-spa

This skill provides guidance for implementing Go Embedded SPA architecture - embedding React/Vue/TSX frontend static resources into Go binary using go:embed directive. Use this skill when building self-contained single-binary applications, implementing SPA with Go backend, setting up cross-platform deployable full-stack projects, or configuring static file serving with Go standard library net/http.

castle-x/skills-x1 installsMITSynced Aug 22

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI
---
name: go-embedded-spa
description: This skill provides guidance for implementing Go Embedded SPA architecture - embedding React/Vue/TSX frontend static resources into Go binary using go:embed directive. Use this skill when building self-contained single-binary applications, implementing SPA with Go backend, setting up cross-platform deployable full-stack projects, or configuring static file serving with Go standard library net/http.
license: MIT
---

# Go Embedded SPA

## Overview

Go Embedded SPA is a technique that embeds frontend SPA (Single Page Application) static resources (React/Vue/TSX) into Go binary files using Go 1.16+ `embed` package, achieving **single-binary full-stack deployment**.

### Core Benefits

| Benefit | Description |
|---------|-------------|
| 🎯 Single File Deploy | One binary contains both frontend and backend, no nginx needed |
| 🌍 Cross-Platform | `GOOS/GOARCH` easily compiles for Linux/Mac/Windows |
| πŸ“¦ Zero Dependencies | Target machine needs no Node.js/npm, uses Go standard library only |
| πŸš€ Container Friendly | Dockerfile only needs `COPY + ENTRYPOINT` |
| πŸ”’ Resource Security | Static resources compiled into binary, tamper-proof |
| ⚑ Fast Startup | No disk I/O for loading static files |

## Project Structure

```
project/
β”œβ”€β”€ go.mod
β”œβ”€β”€ Makefile
β”œβ”€β”€ site/                    # Frontend project
β”‚   β”œβ”€β”€ embed.go             # Go embed directive
β”‚   β”œβ”€β”€ src/                 # React/Vue source
β”‚   β”œβ”€β”€ dist/                # Build output (embedded)
β”‚   β”œβ”€β”€ package.json
β”‚   β”œβ”€β”€ vite.config.ts
β”‚   └── index.html
β”œβ”€β”€ pkg/
β”‚   └── siteserver/          # Static file server
β”‚       └── siteserver.go
└── cmd/
    └── app/
        └── main.go
```

## Implementation Steps

### Step 1: Create embed.go

Create `site/embed.go` to declare embed directive. See `references/embed.md` for complete code.

Key points:
- Use `//go:embed all:dist` to embed all files including hidden files
- Use `fs.Sub()` to remove `dist/` prefix

### Step 2: Create Static File Server

Create `pkg/siteserver/siteserver.go`. See `references/siteserver.md` for complete implementation using Go standard `net/http`.

Core logic:
1. Pre-load `index.html` for SPA fallback
2. Create `http.FileServer` from embed.FS
3. Detect static resources by file extension
4. Return `index.html` for SPA routes (no file extension)

### Step 3: Application Integration

```go
package main

import (
    "log"
    "net/http"
    
    "your-project/pkg/siteserver"
    "your-project/site"
)

func main() {
    mux := http.NewServeMux()
    
    // 1. Register API routes FIRST
    mux.HandleFunc("/apis/v1/health", healthHandler)
    mux.HandleFunc("/apis/v1/data", dataHandler)
    
    // 2. Wrap with static file server (as fallback)
    handler := siteserver.WrapHandler(mux, site.DistDirFS)
    
    log.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", handler))
}
```

**Order is critical:** API routes must be registered before static file server.

### Step 4: Development

**Recommended:** Start both backend and frontend with one command:

```bash
make dev
# Backend:  http://localhost:8080 (Go)
# Frontend: http://localhost:5173 (Vite with hot reload)
# API Proxy: /api/* -> localhost:8080
```

Press `Ctrl+C` to stop both servers.

### Step 5: Build for Production

Build order: **frontend first, then backend**

```bash
make build          # Build both (frontend + backend)
# Or separately:
make build-web      # npm run build β†’ site/dist/
make build-backend  # go build (embeds dist/)
```

### Step 6: Cross-Platform Build

```bash
make build-linux        # Linux amd64
make build-linux-arm64  # Linux arm64
make build-macos        # macOS Intel
make build-macos-arm64  # macOS Apple Silicon
make build-windows      # Windows amd64
make build-all          # All platforms
```

## Request Handling Flow

```
Browser Request
      β”‚
      β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  http.ServeMux Route Matching       β”‚
β”‚  β”œβ”€β”€ /apis/*  β†’ API Handler         β”‚
β”‚  └── Others   β†’ Static Handler      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚
              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Static Handler                     β”‚
β”‚  β”œβ”€β”€ Has extension β†’ serve file     β”‚
β”‚  └── No extension β†’ return index.htmlβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

## Caching Strategy

| Path | Cache-Control | Reason |
|------|---------------|--------|
| `/assets/*` | `max-age=31536000, immutable` | Files have hash in name |
| `/index.html` | `no-cache` | Entry must be fresh |

## Container Deployment

Minimal Dockerfile:

```dockerfile
FROM scratch
COPY app /app
ENTRYPOINT ["/app"]
```

## Troubleshooting

1. **Empty dist error** β†’ Run `make build-web` before `make build-backend`
2. **Static files 404** β†’ Check `//go:embed all:dist` path relative to embed.go
3. **API not matching** β†’ Register API routes BEFORE wrapping with siteserver
4. **SPA routes 404** β†’ Verify handler returns index.html for non-file paths

## References

- `go-dependencies.md` - Go module dependencies (standard library only)
- `embed.md` - Complete embed.go implementation
- `siteserver.md` - Static file server using Go standard net/http
- `vite-config.md` - Vite configuration for development proxy
- `makefile.md` - Complete Makefile with cross-platform build

More Backend Frameworks skills

← All Backend Frameworks skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free Β· No signup Β· No trial clock

SEE THE DIRECTORY