# The Ultimate Guide to `go:embed` Syntax and Usage
> Series: Go Embedding Features — The Ultimate Guide to `go:embed` Syntax and Usage
Since the release of Go 1.16, the `embed` package has become part of the standard library, allowing developers to embed static resources directly into the compiled binary. Whether you're building a web application, CLI tool, or want to avoid external file dependencies during deployment, `go:embed` is a powerful asset that should not be overlooked.
---
## 1. Detailed Syntax Overview
### 1.1 Importing the `embed` Package
```go
import "embed"
You must explicitly import the embed
package, even if you only use its annotations.
1.2 Three Supported Variable Types
Type | Description |
---|---|
string |
Reads file content as UTF-8 encoded text |
[]byte |
Raw binary file data |
embed.FS |
Virtual read-only file system for multiple files or directories |
1.3 Example: Embedding a Single File
import _ "embed"
//go:embed hello.txt
var helloText string
fmt.Println(helloText)
Text is embedded as UTF-8. You can also embed binary files:
//go:embed logo.png
var logo []byte
1.4 Example: Embedding Multiple Files (Wildcard)
//go:embed static/*.js static/*.css
var assets embed.FS
Supports glob patterns (wildcards), but not recursive ones like **
. The glob matches paths, not file content.
1.5 Example: Embedding a Directory
//go:embed templates/*
var tmplFS embed.FS
This is ideal for web projects or template engines.
2. Practical Use Cases
2.1 Build a Self-Contained Web App
//go:embed static/*
var staticFiles embed.FS
func main() {
fs := http.FS(staticFiles)
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(fs)))
log.Fatal(http.ListenAndServe(":8080", nil))
}
No need to deploy the static/
directory separately—the app binary includes it all.
2.2 Embed and Render HTML Templates
//go:embed templates/*.html
var tmplFS embed.FS
func renderTemplate(w http.ResponseWriter) {
t := template.Must(template.ParseFS(tmplFS, "templates/index.html"))
t.Execute(w, map[string]string{"Title": "Welcome!"})
}
Perfect for use with html/template
or text/template
.
2.3 Embed Default Config Files
//go:embed config.yaml
var configYAML []byte
func loadDefaultConfig() Config {
var c Config
_ = yaml.Unmarshal(configYAML, &c)
return c
}
Great for CLI apps that need a default configuration, which users can override via --config
.
2.4 Embed SQL or Migration Scripts
//go:embed migrations/*.sql
var migrationFS embed.FS
func runMigrations(db *sql.DB) {
files, _ := fs.ReadDir(migrationFS, "migrations")
for _, f := range files {
data, _ := migrationFS.ReadFile("migrations/" + f.Name())
db.Exec(string(data))
}
}
Perfect for tools like gorm
, sqlc
, or golang-migrate
.
3. Working with embed.FS
3.1 ReadFile
data, err := fs.ReadFile(embedFS, "templates/index.html")
Returns the file as []byte
, suitable for parsers or direct output.
3.2 ReadDir
entries, err := fs.ReadDir(embedFS, "static")
for _, entry := range entries {
fmt.Println(entry.Name())
}
Similar to os.ReadDir
, but operates on the embedded filesystem.
3.3 Open File Handle
f, err := embedFS.Open("static/logo.png")
defer f.Close()
io.Copy(w, f) // Directly write to HTTP response
Note: embed.FS
is read-only—you cannot modify embedded files.
4. Limitations and Caveats
Limitation | Description |
---|---|
Compile-Time Embedding Only | Files must exist at build time; runtime changes are not supported |
No Recursive Globs (** ) |
You must list each subdirectory explicitly or use specific patterns (e.g. *.ext ) |
Relative Paths | Paths are relative to the Go file, not the project root |
Read-Only Access | Embedded files are immutable and cannot be written to |
No Dynamic Paths | //go:embed paths must be string constants, not variables or expressions |
5. Comparison with Third-Party Tools
Feature | go:embed |
rice | statik |
---|---|---|---|
Built-in | ✅ Yes | ❌ No | ❌ No |
Supports Directory | ✅ Yes | ✅ Yes | ✅ Yes |
Hot Reload Support | ❌ No | ✅ Yes (dev mode) | ❌ No |
Build Performance | ✅ Fast | ❌ Slower (requires code generation) | ❌ Slower |
Ease of Use | ✅ Simple | ❌ Complex | ❌ Moderate |
Recommendation: use go:embed
for all small to medium projects; only consider rice
if hot reloading is essential.
6. Debugging Tips & Common Pitfalls
6.1 Embedded File Not Found?
- Ensure the
go:embed
comment is placed directly above the variable. - Confirm the path is relative to the
.go
file containing the directive. - File names and extensions are case-sensitive.
6.2 File Not Updating?
Embedded resources are locked at build time. Rebuild the binary after changing embedded files.
6.3 UTF-8 Issues with Chinese or Non-ASCII Characters?
Make sure the embedded files are saved as UTF-8. If reading []byte
, manually convert:
str := string(data) // assuming UTF-8 encoding
7. Best Practices Summary
✅ Use embed.FS
for multi-file or directory-based resource management
✅ Organize static resources into folders like static/
, templates/
, or assets/
✅ Prefer go:embed
over external dependencies or Docker bind mounts
✅ Combine with go generate
or checksum checks for enhanced build workflows
Example Projects
Conclusion
go:embed
represents a significant step toward standardized, modular resource management in Go. It greatly simplifies deployment for CLI apps, web servers, and containerized applications. If you’re still manually copying static files during deployment—now’s the time to embrace go:embed
.