Skip to main content

Templated HTML + JS Page

This recipe shows a normal server-rendered page shape:

  • Zinc renders the initial HTML
  • Zinc serves browser assets from /static
  • a small JavaScript file progressively enhances the page after load

Setup

go mod init zinc-web-page
go get github.com/0mjs/zinc

Project layout

.
├── main.go
├── public
│ └── app.js
└── templates
└── home.html

Application

package main

import (
"html/template"
"log"

"github.com/0mjs/zinc"
)

func main() {
views := template.Must(template.ParseGlob("templates/*.html"))

app := zinc.NewWithConfig(zinc.Config{
Renderer: zinc.NewHTMLTemplateRenderer(
views,
zinc.WithTemplateSuffixes(".html"),
),
})

if err := app.Static("/static", "./public"); err != nil {
log.Fatal(err)
}

app.Get("/", func(c *zinc.Context) error {
return c.Render("home", zinc.Map{
"Title": "Zinc Web Page",
"Heading": "Hello from Zinc",
"User": "Matt",
"Theme": "graphite",
})
})

app.Listen()
}

Template

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{{ .Title }}</title>
</head>
<body>
<main id="app" data-user="{{ .User }}" data-theme="{{ .Theme }}">
<h1>{{ .Heading }}</h1>
<p>The first render comes from Zinc templates.</p>
<p id="status">Loading browser behavior...</p>
</main>

<script src="/static/app.js" defer></script>
</body>
</html>

Browser script

const app = document.querySelector("#app");
const status = document.querySelector("#status");

if (app && status) {
const user = app.dataset.user || "friend";
const theme = app.dataset.theme || "default";

status.textContent = `Ready. Welcome, ${user}. Theme: ${theme}.`;
}

Run it

go run .
# open http://localhost:8080

Why this recipe matters

This is the cleanest pattern when you want:

  • server-rendered HTML
  • a little client-side behavior
  • normal static asset handling
  • no SPA runtime overhead