Skip to main content
In this guide, you’ll create a simple web page with a layout and dynamic content. By the end, you’ll understand the core concepts of Mizu’s view system.

Prerequisites

  • Go 1.22 or later installed
  • Basic understanding of Go and HTML
  • A text editor

Step 1: Create Your Project

First, create a new directory and initialize a Go module:
mkdir myapp && cd myapp
go mod init myapp
go get github.com/go-mizu/mizu

Step 2: Create the Directory Structure

The view package expects templates in a specific structure:
mkdir -p views/layouts views/pages
Your project should look like this:
myapp/
├── go.mod
├── go.sum
├── main.go (we'll create this)
└── views/
    ├── layouts/
    │   └── default.html
    └── pages/
        ├── home.html
        └── about.html

Step 3: Create the Layout

A layout is the HTML shell that wraps your pages. Create views/layouts/default.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{.Data.Title}}</title>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background: #f5f5f5;
        }
        nav {
            background: #333;
            color: white;
            padding: 15px;
            margin: -20px -20px 20px -20px;
        }
        nav a {
            color: white;
            text-decoration: none;
            margin-right: 15px;
        }
        main {
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        footer {
            text-align: center;
            margin-top: 20px;
            color: #666;
        }
    </style>
</head>
<body>
    <nav>
        <a href="/">Home</a>
        <a href="/about">About</a>
    </nav>
    <main>
        {{.Content}}
    </main>
    <footer>
        Built with Mizu
    </footer>
</body>
</html>
What’s happening here?
  • {{.Data.Title}} - Accesses the “Title” field from data you pass
  • {{.Content}} - This is where the page content gets inserted
  • The layout provides the common structure (doctype, head, nav, footer)

Step 4: Create Pages

Pages are templates that provide the main content. Create views/pages/home.html:
<h1>Hello, {{.Data.Name}}!</h1>

<p>Welcome to our website. We're glad you're here.</p>

<h2>What We Offer</h2>
<ul>
    {{range .Data.Features}}
    <li>{{.}}</li>
    {{end}}
</ul>

<button style="background: #2563eb; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer;">
    Get Started
</button>
What’s happening here?
  • {{.Data.Name}} - Accesses data passed from your handler
  • {{range .Data.Features}}...{{end}} - Loops through a list
  • {{.}} - Inside range, this is the current item
  • Page content is automatically inserted into the layout’s {{.Content}}
Create views/pages/about.html:
<h1>About Us</h1>

<p>We are a team passionate about building great web applications with Go.</p>

<h2>Our Mission</h2>
<p>To make server-side rendering in Go simple, fast, and enjoyable.</p>

Step 5: Create the Go Application

Now create main.go:
package main

import (
    "github.com/go-mizu/mizu"
    "github.com/go-mizu/mizu/view"
)

func main() {
    // Step 1: Create the view engine
    engine := view.New(view.Config{
        Dir:         "./views",   // Where templates are stored
        Extension:   ".html",     // File extension to look for
        Development: true,        // Enable hot reload
    })

    // Step 2: Create the Mizu app
    app := mizu.New()

    // Step 3: Add the view middleware
    // This makes the engine available to all handlers
    app.Use(engine.Middleware())

    // Step 4: Define routes
    app.Get("/", homeHandler)
    app.Get("/about", aboutHandler)

    // Step 5: Start the server
    app.Listen(":8080")
}

func homeHandler(c *mizu.Ctx) error {
    return view.Render(c, "home", view.Data{
        "Title": "Home - My App",
        "Name":  "Friend",
        "Features": []string{
            "Fast server-side rendering",
            "Layout support",
            "Development hot reload",
            "Production caching",
        },
    })
}

func aboutHandler(c *mizu.Ctx) error {
    return view.Render(c, "about", view.Data{
        "Title": "About - My App",
    })
}
What’s happening here?
  1. Create engine - view.New() creates a view engine with your configuration
  2. Add middleware - app.Use(engine.Middleware()) stores the engine in every request context
  3. Render pages - view.Render(c, "home", data) renders the “home” page with data

Step 6: Run Your Application

Start the server:
go run main.go
Now open your browser to http://localhost:8080. You should see:
  • The home page with your name and features list
  • A styled button
  • Navigation to the About page
Try editing views/pages/home.html and refreshing your browser - the changes appear instantly because Development mode is enabled!

Understanding the Data Flow

Here’s what happens when someone visits your site:
1. Browser requests http://localhost:8080/
                    |
                    v
2. Mizu routes to homeHandler
                    |
                    v
3. Handler calls view.Render(c, "home", data)
                    |
                    v
4. View engine finds views/pages/home.html
                    |
                    v
5. Engine also loads views/layouts/default.html
                    |
                    v
6. Page is rendered, output goes into layout's {{.Content}}
                    |
                    v
7. Final HTML sent to browser

Understanding Template Data

Templates receive a wrapper structure, not just your data directly:
// In your handler:
view.Render(c, "home", view.Data{
    "Title": "Home",
    "User":  user,
})
<!-- In templates, access with .Data prefix: -->
<h1>{{.Data.Title}}</h1>
<p>{{.Data.User.Name}}</p>

<!-- The full structure: -->
<!-- .Page.Name   = "home" (template name) -->
<!-- .Page.Layout = "default" (layout name) -->
<!-- .Data        = your data map -->
<!-- .Content     = rendered page (only in layouts) -->

Common Patterns

Using a Different Layout

Override the default layout for specific pages:
view.Render(c, "dashboard", data, view.Layout("admin"))
This uses views/layouts/admin.html instead of default.html.

Rendering Without a Layout

For partial HTML responses (AJAX, htmx, etc.):
view.Render(c, "user-row", data, view.NoLayout())

Using the Engine Directly

If you need the engine instance:
func handler(c *mizu.Ctx) error {
    engine := view.From(c)
    return engine.Render(c.Writer(), "home", data)
}

Next Steps

You’ve learned the basics! Here’s what to explore next:
  • Engine - All configuration options explained
  • Templates - Deep dive into Go template syntax
  • Layouts - Advanced layout patterns
  • Functions - Built-in and custom template functions
  • Production - Embedding templates and caching for production

Troubleshooting

”template not found” Error

Check that:
  1. Your views directory exists with layouts/ and pages/ subdirectories
  2. The file extension matches your Config (default is .html)
  3. The name in Render() matches the filename in pages/ (without extension)

Changes Not Appearing

Make sure Development: true is set in your view config. In production mode (the default), templates are cached.

”layout not found” Error

Make sure the layout file exists at views/layouts/default.html (or your custom DefaultLayout value).