Question
In the Go language specification, struct fields may include an optional string literal called a tag. The specification says these tags are exposed through reflection but are otherwise ignored by the language itself.
For example:
// A struct corresponding to the TimeStamp protocol buffer.
// The tag strings define the protocol buffer field numbers.
type TimeStamp struct {
Microsec uint64 `field:"1"`
ServerIP6 uint64 `field:"2"`
Process string `field:"3"`
}
What are struct tags used for in Go, and why are they useful in practice?
Short Answer
By the end of this page, you will understand what struct tags are in Go, why the Go compiler mostly ignores them, and how libraries use reflection to read them. You will also see common uses such as JSON encoding, database mapping, validation, and configuration parsing.
Concept
Struct tags in Go are metadata attached to struct fields.
A struct field can be followed by a string literal, usually written with backticks:
type User struct {
Name string `json:"name"`
}
The Go language itself does not give built-in meaning to most tags. Instead:
- the tag is stored with the field definition
- packages can inspect it using the
reflectpackage - those packages decide what the tag means
That is why the specification says tags are "made visible through a reflection interface but are otherwise ignored."
Why this matters
Struct tags let you keep data structure definitions and mapping rules together.
Without tags, libraries would need separate configuration to answer questions like:
- What JSON key should this field use?
- Should this field be skipped if empty?
- Which database column matches this field?
- Is this field required?
Tags make those rules compact and close to the code they affect.
Common examples
JSON serialization
type User struct {
FirstName string `json:"first_name"`
Password string `json:"-"`
}
Mental Model
Think of a struct tag like a sticky note attached to a field.
The field is the actual data box. The tag is a note that says something about how another tool should treat that box.
For example:
- the JSON tool reads the note and says, "I should export this field as
first_name" - a validator reads the note and says, "This field is required"
- a database mapper reads the note and says, "This matches the
user_idcolumn"
The Go runtime stores the sticky note, but Go itself does not usually act on it. Other packages choose whether to read it.
Syntax and Examples
Basic syntax
Struct tags are string literals placed after a field declaration.
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
The conventional format is a space-separated list of key:"value" pairs inside a raw string literal.
`json:"name,omitempty" db:"full_name" validate:"required"`
Example: using encoding/json
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Password string `json:"-"`
}
func main() {
user := User{
ID: 1,
Name: "Ava",
Password: "secret123",
}
data, _ := json.Marshal(user)
fmt.Println((data))
}
Step by Step Execution
Consider this example:
package main
import (
"encoding/json"
"fmt"
)
type Product struct {
Name string `json:"name"`
Price float64 `json:"price,omitempty"`
}
func main() {
p := Product{Name: "Pen"}
data, _ := json.Marshal(p)
fmt.Println(string(data))
}
Step by step
1. The struct type is defined
Product has two fields:
Namewith tagjson:"name"Pricewith tagjson:"price,omitempty"
These tags are stored as metadata.
2. A value is created
p := Product{Name: "Pen"}
At this point:
Nameis
Real World Use Cases
1. JSON APIs
Very common in web servers and REST APIs.
type APIResponse struct {
UserID int `json:"user_id"`
UserName string `json:"user_name"`
}
This lets your Go naming style stay idiomatic while your API uses a different format.
2. Database row mapping
Libraries often map columns to fields using tags.
type Order struct {
ID int `db:"id"`
Customer string `db:"customer_name"`
CreatedAt string `db:"created_at"`
}
Useful when database column names and Go field names do not match exactly.
3. Validation of incoming data
type LoginRequest struct {
Email string `validate:"required,email"`
Password string `validate:"required"`
}
Validation libraries can reject bad input before business logic runs.
4. XML, YAML, TOML, and configuration files
Different serializers use their own tag keys:
Real Codebase Usage
In real Go projects, struct tags are usually combined with predictable patterns.
DTOs and API models
Developers often define request and response structs with JSON tags:
type CreateUserRequest struct {
Email string `json:"email" validate:"required,email"`
Name string `json:"name" validate:"required"`
}
This keeps API parsing and validation close together.
Guard clauses after decoding
A common pattern is:
- decode JSON into a tagged struct
- validate fields
- return early if invalid
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return err
}
if req.Email == "" {
return errors.New("email is required")
}
Multiple tags on one field
Real code often uses several libraries on the same struct:
type User struct {
ID int `json:"id" db:"id"`
Email string `json:"email" db:"email" validate:"required,email"`
}
This is convenient, but too many responsibilities in one struct can become hard to manage.
Common Mistakes
1. Thinking tags do something by themselves
A tag alone has no behavior.
type User struct {
Name string `mytag:"important"`
}
This does nothing unless a package reads mytag.
2. Expecting unexported fields to work with many libraries
Many reflection-based packages only work with exported fields.
Broken example:
type User struct {
name string `json:"name"`
}
Even with a tag, name is unexported, so encoding/json will ignore it.
Correct:
type User struct {
Name string `json:"name"`
}
3. Using invalid tag formatting
Broken example:
type User struct {
Name string
}
Comparisons
Struct tags vs regular field names
| Approach | What it does | When useful | Limitation |
|---|---|---|---|
| Field name only | Uses Go field name as-is | Simple cases | External names may not match |
| Struct tag | Adds metadata for libraries | JSON, DB, validation, config | Needs reflection-aware code |
Struct tags vs separate configuration
| Approach | Pros | Cons |
|---|---|---|
| Struct tags | Close to the code, compact, easy to read | Can clutter structs |
| Separate config files or mappings | Keeps structs cleaner, can be centralized | Harder to track and maintain |
Built-in language behavior vs library-defined behavior
Cheat Sheet
Quick reference
Basic syntax
type Example struct {
Field string `key:"value"`
}
Multiple tags
type User struct {
Name string `json:"name" db:"full_name" validate:"required"`
}
Read a tag with reflection
field.Tag.Get("json")
Common JSON tag values
`json:"name"` // use "name" as JSON key
`json:"name,omitempty"` // omit if zero value
`json:"-"` // ignore field
Rules to remember
- Tags are metadata on struct fields.
- Go stores them, but most meaning comes from libraries.
- Reflection is used to inspect them.
- Many libraries only use exported fields.
- Tag format is usually
key:"value". - Different packages interpret tags differently.
Common zero values relevant to omitempty
FAQ
What are struct tags in Go?
Struct tags are string-based metadata attached to struct fields. Libraries can read them with reflection to control behavior such as JSON encoding, validation, or database mapping.
Does Go itself use struct tags?
Mostly no. Go stores them, but the language does not usually act on them directly. Packages and frameworks decide what they mean.
How do I read a struct tag in Go?
Use the reflect package. A field's tag can be accessed with field.Tag.Get("key").
Why are backticks used for struct tags?
Backticks create a raw string literal, which makes it easier to write quotes inside the tag like json:"name".
Can I define my own struct tags?
Yes. You can create any tag key you want, such as config:"port", as long as your code or a library reads it.
Why is my JSON tag not working?
A common reason is that the field is unexported. For example, name string will be ignored by encoding/json, even if it has a json tag.
Can one field have multiple struct tags?
Yes. A single field can include several tags for different tools, such as json, db, and validate.
Mini Project
Description
Build a small Go program that converts a struct into JSON and also inspects its tags with reflection. This demonstrates the two most important ideas: tags affect library behavior only when a package reads them, and you can also read tags yourself.
Goal
Create a program that prints JSON output from a tagged struct and then lists each field's tag metadata.
Requirements
- Define a struct with at least three exported fields.
- Add
jsontags to control the JSON key names. - Ignore one field in JSON output using a tag.
- Use
encoding/jsonto marshal the struct. - Use
reflectto print thejsontag for each field.
Keep learning
Related questions
Check if a Value Exists in a Slice in Go
Learn how to check whether a value exists in a slice in Go, and why Go has no Python-style `in` operator for arrays or slices.
Concatenating Slices in Go with append
Learn how to concatenate two slices in Go using append and the ... operator, with examples, pitfalls, and practical usage.
Convert String to Integer in Go: Idiomatic Parsing with strconv.Atoi
Learn the idiomatic way to convert a string to an int in Go using strconv.Atoi, with examples, errors, and common mistakes.