Struct Inference
Learn how goTableView can automatically detect columns from your Go structs, saving you time and reducing errors.
What You'll Learn
- Automatic column detection from structs
- Using struct tags to customize columns
- Working with different data types
- Handling maps and complex data
The Problem
Manually building tables from structs is tedious:
type Employee struct {
Name string
Department string
Age int
Salary float64
}
employees := []Employee{
{"Alice", "Engineering", 30, 75000.00},
{"Bob", "Sales", 25, 60000.00},
}
// Manual approach - repetitive and error-prone ❌
table := gotableview.New("Employees")
table.Columns("Name", "Department", "Age", "Salary")
for _, emp := range employees {
table.Row(
emp.Name,
emp.Department,
fmt.Sprintf("%d", emp.Age),
fmt.Sprintf("%.2f", emp.Salary),
)
}
table.Show()The Solution: FromStructs()
Let goTableView do the work for you:
type Employee struct {
Name string
Department string
Age int
Salary float64
}
employees := []Employee{
{"Alice", "Engineering", 30, 75000.00},
{"Bob", "Sales", 25, 60000.00},
}
// Automatic approach - clean and simple ✅
gotableview.FromStructs("Employees", employees).Show()
How It Works
FromStructs() uses Go's reflection to:
- Detect all exported struct fields
- Use field names as column headers
- Automatically format values based on their types
Basic Struct Inference
Simple Example
package main
import "github.com/mansoldof/goTableView"
type Product struct {
ID int
Name string
Price float64
Stock int
}
func main() {
products := []Product{
{1, "Laptop", 999.99, 15},
{2, "Mouse", 24.99, 50},
{3, "Keyboard", 74.99, 30},
}
gotableview.FromStructs("Product Inventory", products).Show()
}Result:
- Columns:
ID,Name,Price,Stock - Price automatically formatted to 2 decimal places
- No manual conversion needed!
Automatic Type Formatting
goTableView handles common types intelligently:
type Demo struct {
Text string
Integer int
Decimal float64
IsActive bool
Timestamp time.Time
}
demo := []Demo{
{"Hello", 42, 3.14159, true, time.Now()},
{"World", 100, 2.71828, false, time.Now().Add(-24 * time.Hour)},
}
gotableview.FromStructs("Type Demo", demo).Show()Type Formatting:
string→ As-isint,int8,int16,int32,int64→ Integer formatuint,uint8,uint16,uint32,uint64→ Unsigned integerfloat32,float64→ Two decimal places (e.g., "3.14")bool→ "Yes" or "No"time.Time→ "2006-01-02 15:04:05"nilpointers → Empty string
Customizing with Struct Tags
Use the gotableview tag to control column headers:
Basic Tag Usage
type Product struct {
ID int `gotableview:"Product ID"`
Name string `gotableview:"Product Name"`
Price float64 `gotableview:"Price ($)"`
InStock bool `gotableview:"Available"`
Description string `gotableview:"Details"`
}
products := []Product{
{1, "Laptop", 999.99, true, "High-performance laptop"},
{2, "Mouse", 24.99, true, "Wireless mouse"},
}
gotableview.FromStructs("Products", products).Show()Result:
- Column headers:
Product ID,Product Name,Price ($),Available,Details - Much more user-friendly than default field names!

Skipping Fields
Use gotableview:"-" to hide fields:
type User struct {
ID int `gotableview:"User ID"`
Name string `gotableview:"Full Name"`
Email string `gotableview:"Email Address"`
Password string `gotableview:"-"` // Hidden from table
APIKey string `gotableview:"-"` // Hidden from table
Role string `gotableview:"Role"`
}
users := []User{
{1, "Alice Johnson", "alice@example.com", "secret123", "key-xyz", "Admin"},
{2, "Bob Smith", "bob@example.com", "pass456", "key-abc", "User"},
}
gotableview.FromStructs("Users", users).Show()Result:
- Columns shown:
User ID,Full Name,Email Address,Role - Password and APIKey are not displayed ✅
Security Note
Even though sensitive fields are hidden from the table, they're still in memory. Don't use this as a security measure - properly handle sensitive data in your application.
Working with Pointers
goTableView gracefully handles pointer fields:
type Employee struct {
Name string
Age *int // Pointer - may be nil
Salary *float64 // Pointer - may be nil
Active bool
}
age1 := 30
salary1 := 75000.00
employees := []Employee{
{"Alice", &age1, &salary1, true},
{"Bob", nil, nil, false}, // nil pointers display as empty
}
gotableview.FromStructs("Employees", employees).Show()Result:
- Alice: Shows age "30" and salary "75000.00"
- Bob: Shows empty strings for age and salary (nil values)
Nested Structs
Currently, nested structs are displayed using their string representation:
type Address struct {
City string
State string
}
type Person struct {
Name string
Age int
Address Address // Nested struct
}
people := []Person{
{"Alice", 30, Address{"New York", "NY"}},
{"Bob", 25, Address{"Los Angeles", "CA"}},
}
gotableview.FromStructs("People", people).Show()Result:
- Address column shows:
{New York NY}
Better Nested Struct Handling
For better control over nested structs, flatten them or use separate columns:
type Person struct {
Name string `gotableview:"Name"`
Age int `gotableview:"Age"`
City string `gotableview:"City"`
State string `gotableview:"State"`
}Working with Maps
For dynamic data structures, use FromMaps():
package main
import "github.com/mansoldof/goTableView"
func main() {
data := []map[string]any{
{"Name": "Alice", "Age": 30, "City": "NYC"},
{"Name": "Bob", "Age": 25, "City": "LA"},
{"Name": "Carol", "City": "Chicago"}, // Missing "Age"
}
gotableview.FromMaps("People", data).Show()
}Features:
- Columns detected from all map keys
- Missing keys show as empty cells
- Order of columns may vary

Pagination with Structs
For large datasets, combine structs with pagination:
package main
import (
"time"
"fmt"
"github.com/mansoldof/goTableView"
)
func main() {
type LogEntry struct {
Timestamp time.Time `gotableview:"Time"`
Level string `gotableview:"Level"`
Message string `gotableview:"Message"`
}
// Create 1000 log entries
logs := make([]LogEntry, 1000)
for i := range logs {
logs[i] = LogEntry{
Timestamp: time.Now().Add(-time.Duration(i) * time.Minute),
Level: "INFO",
Message: fmt.Sprintf("Log entry #%d", i+1),
}
}
// Show with pagination (50 per page)
gotableview.FromStructsPaginated("Application Logs", logs, 50).
WithSearch(). // Add search functionality
Show()
}
Advanced Examples
Example 1: Database Results
package main
import (
"database/sql"
"time"
"github.com/mansoldof/goTableView"
)
type Order struct {
OrderID int `gotableview:"Order #"`
Customer string `gotableview:"Customer Name"`
Product string `gotableview:"Product"`
Quantity int `gotableview:"Qty"`
Total float64 `gotableview:"Total ($)"`
OrderDate time.Time `gotableview:"Order Date"`
}
func main() {
db, _ := sql.Open("sqlite3", "orders.db")
defer db.Close()
rows, _ := db.Query("SELECT id, customer, product, quantity, total, order_date FROM orders")
defer rows.Close()
var orders []Order
for rows.Next() {
var o Order
rows.Scan(&o.OrderID, &o.Customer, &o.Product, &o.Quantity, &o.Total, &o.OrderDate)
orders = append(orders, o)
}
gotableview.FromStructs("Recent Orders", orders).Show()
}Example 2: JSON API Response
package main
import (
"encoding/json"
"net/http"
"github.com/mansoldof/goTableView"
)
type User struct {
ID int `json:"id" gotableview:"User ID"`
Name string `json:"name" gotableview:"Name"`
Username string `json:"username" gotableview:"Username"`
Email string `json:"email" gotableview:"Email"`
Phone string `json:"phone" gotableview:"Phone"`
}
func main() {
resp, _ := http.Get("https://jsonplaceholder.typicode.com/users")
defer resp.Body.Close()
var users []User
json.NewDecoder(resp.Body).Decode(&users)
gotableview.FromStructs("API Users", users).Show()
}Example 3: File System Scanner
package main
import (
"os"
"path/filepath"
"time"
"github.com/mansoldof/goTableView"
)
type FileInfo struct {
Name string `gotableview:"File Name"`
Size int64 `gotableview:"Size (bytes)"`
Modified time.Time `gotableview:"Last Modified"`
IsDir bool `gotableview:"Directory"`
}
func main() {
var files []FileInfo
filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil
}
files = append(files, FileInfo{
Name: info.Name(),
Size: info.Size(),
Modified: info.ModTime(),
IsDir: info.IsDir(),
})
return nil
})
gotableview.FromStructsPaginated("File System", files, 100).
WithSearch().
Show()
}Best Practices
- Use meaningful field names - They become column headers
- Add struct tags for clarity - Make headers user-friendly
- Hide sensitive fields - Use
gotableview:"-"for passwords, keys - Consider pagination - Use
FromStructsPaginated()for 100+ items - Export fields - Only exported (capitalized) fields are shown
Common Patterns
Configuration Structs
type Config struct {
ServerHost string `gotableview:"Server"`
ServerPort int `gotableview:"Port"`
DBName string `gotableview:"Database"`
DebugMode bool `gotableview:"Debug Enabled"`
Timeout int `gotableview:"Timeout (s)"`
}
config := Config{
ServerHost: "localhost",
ServerPort: 8080,
DBName: "myapp",
DebugMode: true,
Timeout: 30,
}
// Show single config as a slice with one item
gotableview.FromStructs("Configuration", []Config{config}).Show()Converting Existing Code
Before (manual):
for _, item := range items {
table.Row(
fmt.Sprintf("%d", item.ID),
item.Name,
fmt.Sprintf("%.2f", item.Price),
)
}After (automatic):
gotableview.FromStructs("Items", items).Show()What's Next?
- Learn about Pagination & Search for large datasets
- See Advanced Examples
- Check the API Reference for
FromStructs()details
Performance Note
Struct reflection has minimal overhead. For datasets under 10,000 items, the performance impact is negligible.