π¦ Go embed.FS
Tutorial
Introduced in Go 1.16,
embed.FS
revolutionizes how static assets are managed. This tutorial covers everything from the basics to advanced use cases, with practical examples and solutions.
β¨ Why Use embed.FS
?
In web projects, CLI tools, config systems, or template engines, you often need to load static files like:
- HTML / CSS / JS pages
- YAML / JSON config files
- SQL initialization scripts
- Text templates or Markdown files
- Image assets (PNG, SVG, etc.)
Previously, tools like go-bindata
or vfsgen
were commonly used to embed files into Go binaries. However, they:
- Require extra build steps
- Are harder to debug and maintain
- Introduce non-standard build flows
With Go 1.16, the official embed
package offers built-in, safe, cross-platform, and dependency-free asset embedding.
π Basic Usage
1οΈβ£ Embed a Single File
import "embed"
//go:embed hello.txt
var f embed.FS
func main() {
data, _ := f.ReadFile("hello.txt")
fmt.Println(string(data))
}
Just add a //go:embed
directive β no code generation or external tools needed.
2οΈβ£ Embed Entire Directories or Multiple Files
//go:embed templates/*.tmpl static/*
var assets embed.FS
data, _ := assets.ReadFile("templates/header.tmpl")
- Supports wildcards (
*
,**
) - Use relative paths consistent with your project layout
3οΈβ£ Use with html/template
//go:embed templates/*
var tmplFS embed.FS
func main() {
tmpl := template.Must(template.ParseFS(tmplFS, "templates/index.tmpl"))
tmpl.Execute(os.Stdout, map[string]string{"Title": "Hello, embed"})
}
π‘ Advanced Patterns & Real-World Usage
1οΈβ£ Serve Static Assets Over HTTP
//go:embed static
var staticFS embed.FS
func main() {
subFS, _ := fs.Sub(staticFS, "static")
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(subFS))))
http.ListenAndServe(":8080", nil)
}
Use fs.Sub()
to set a virtual root for static resources β perfect for embedded frontend files.
2οΈβ£ Create a Resource Manager Wrapper
type AssetManager struct {
fs fs.FS
}
func NewAssetManager(f fs.FS) *AssetManager {
return &AssetManager{fs: f}
}
func (am *AssetManager) MustRead(path string) string {
data, err := am.fs.ReadFile(path)
if err != nil {
panic(err)
}
return string(data)
}
// Usage
//go:embed config/*
var configFS embed.FS
var Config = NewAssetManager(configFS)
fmt.Println(Config.MustRead("config/app.yaml"))
3οΈβ£ Support Hot Reloading in Dev Mode
func readFile(devPath string, embedFS fs.FS, path string) ([]byte, error) {
if _, err := os.Stat(devPath); err == nil {
return os.ReadFile(devPath)
}
return embedFS.ReadFile(path)
}
Load from disk during development, and fall back to embed.FS
in production β supporting both hot reloads and packaging.
4οΈβ£ Embed Gzipped Resources for Better Performance
//go:embed static/*.gz
var gzFS embed.FS
func serveGzip(w http.ResponseWriter, r *http.Request) {
data, _ := gzFS.ReadFile("static/app.js.gz")
w.Header().Set("Content-Encoding", "gzip")
w.Header().Set("Content-Type", "application/javascript")
w.Write(data)
}
Pre-compress assets and serve them directly β ideal for high-performance web responses.
5οΈβ£ Use with VFS Libraries (e.g., Afero)
import "github.com/spf13/afero"
func FSAdapter(fsys fs.FS) afero.Fs {
return afero.NewReadOnlyFs(afero.NewIOFS(fsys))
}
Wrap embed.FS
to use with virtual file system libraries β useful for plugin systems or cross-environment compatibility.
π Project Layout Example
myapp/
βββ main.go
βββ static/
β βββ app.js
βββ templates/
β βββ index.tmpl
βββ config/
β βββ app.yaml
In main.go
:
//go:embed static templates config
var appFS embed.FS
Then use:
tmpl := template.Must(template.ParseFS(appFS, "templates/*.tmpl"))
tmpl.ExecuteTemplate(w, "index.tmpl", data)
π Caveats & Limitations
Limitation | Details |
---|---|
Only supports relative paths | ../ is not allowed |
Files must exist at compile time | Runtime-generated files canβt be embedded |
Empty directories are ignored | Embed skips folders with no files |
Must be declared at package level | Cannot declare embed variables inside functions |
β
When Should You Use embed.FS
?
Scenario | Recommendation |
---|---|
CLI tools with built-in templates/config | β Highly recommended |
Embedding web assets in Go servers | β Recommended |
Scaffolding generators with templates | β Recommended |
Live reload in dev environment | β οΈ Needs fallback or dev-mode toggle |
Embedding large media files (>10MB) | β Not recommended (use CDN/storage) |
π References
- Go Official Docs: https://pkg.go.dev/embed
- Go 1.16 Release Notes: https://golang.org/doc/go1.16#embed