| 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. |
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:
- Pre-load
index.html for SPA fallback
- Create
http.FileServer from embed.FS
- Detect static resources by file extension
- Return
index.html for SPA routes (no file extension)
Step 3: Application Integration
package main
import (
"log"
"net/http"
"your-project/pkg/siteserver"
"your-project/site"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/apis/v1/health", healthHandler)
mux.HandleFunc("/apis/v1/data", dataHandler)
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:
make dev
Press Ctrl+C to stop both servers.
Step 5: Build for Production
Build order: frontend first, then backend
make build
make build-web
make build-backend
Step 6: Cross-Platform Build
make build-linux
make build-linux-arm64
make build-macos
make build-macos-arm64
make build-windows
make build-all
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:
FROM scratch
COPY app /app
ENTRYPOINT ["/app"]
Troubleshooting
- Empty dist error → Run
make build-web before make build-backend
- Static files 404 → Check
//go:embed all:dist path relative to embed.go
- API not matching → Register API routes BEFORE wrapping with siteserver
- 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