Introduction
Astro is a modern static site generator that allows you to build fast, modern websites using a component-based architecture. More details about Astro at https://astro.build
Go templates are a powerful tool for creating dynamic content and reusable components in your Go applications. More details about Go templates at https://pkg.go.dev/text/template
At the point of writing this article, Astro does not support Golang as a SSR adapter. So we will use use Go templates to inject dynamic content in your Astro projects by serving the static files generated by Astro with a Go server.
It doesn’t even have to be Astro - any static site generator will work - but we will focus on Astro in this article as it’s our favorite.
Prerequisites
Before you begin, make sure you have the following prerequisites:
- Go installed on your machine. Getting started with Golang: https://go.dev/doc/tutorial/getting-started
- Node.js installed on your machine: Getting started with NodeJS: https://nodejs.org/en/download/
Setup
Initialize a new Go project:
go mod init github.com/wingravity/go-astroCreate a new Go file named main.go:
package main
import (
"log"
"net/http"
)
func main() {
app := NewRouter()
tmp := NewTemplatesHandler()
app.mux.Handle("GET /", http.HandlerFunc(tmp.IndexView))
log.Fatal(app.ListenAndServe(":8080"))
}
Here we have a simple Go program that initializes a new router and a templates handler.
The router listens on port 8080.
Let’s create the router and templates handler in a new Go file named router.go:
package main
import (
"net/http"
"text/template"
)
type Router struct {
mux *http.ServeMux
}
func NewRouter() *Router {
return &Router{
mux: http.NewServeMux(),
}
}
func (r *Router) ListenAndServe(address string) error {
return http.ListenAndServe(address, r.mux)
}
type ViewsHandler struct {
Index *template.Template
}
func NewTemplatesHandler() *ViewsHandler {
return &ViewsHandler{
// IMPORTANT: We specify custom delimiters for Go templates,
// so they don't conflict with Astro's delimiters {{ and }}
Index: template.Must(template.New("index").Delims("[[", "]]").ParseFiles("client/dist/index.html")),
}
}
type IndexParams struct {
Name string
}
func (h *ViewsHandler) IndexView(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
h.Index.ExecuteTemplate(w, "index.html", IndexParams{Name: "Go Astro"})
return
}
http.ServeFile(w, r, "client/dist"+r.URL.Path)
}
The IndexView method in the ViewsHandler struct renders the index.html template with the name “Go Astro”. Here, we can inject dynamic content into the Astro-generated static files. This can be any data you want to pass to the template.
We also specify custom delimiters for Go templates so they don’t conflict with Astro’s delimiters.
Now let’s add the client files generated by Astro to the project. Let’s initialize a new Astro project:
npm create astro@latestPut the Astro project in the client directory:
- Where should we create your new project? ./client
Respond to the other questions as you see fit.
What we’re interested in is the index page, as this is the one we will be injecting dynamic content into.
Create a new file named index.astro under client/src/pages and add the following content (you can remove the existing content):
---
import { Image } from "astro:assets";
import logo from "../astro-logo-dark.png";
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<Image src={logo} alt="LOGO" width={200} />
<!-- This is where the dynamic content will be injected. -->
<h1>Hello [[.Name]]</h1>
</body>
</html>
There are 2 important things to note here:
- We use
[[.Name]]as the template variable for the name. This is because we have specified custom delimiters for Go templates in therouter.gofile. - We left in the Astro logo on purpose. This is because we also need to serve the static assets generated by Astro with the Go server.
Now we need to build the Astro project:
cd client
npm run build
This will generate the static files in the client/dist directory.
Now we need to run the Go server:
go run .Open your browser and navigate to https://localhost:8080. You should see the Astro logo and the text “Hello Go Astro”.

This experiment is available as a GitHub repository for you to clone and play around with.
Conclusion
This method introduces some dynamism to your Astro projects and can be expanded to include components, similar to the GoTH stack. You also have full flexibility on how to handle routing, data fetching and other server-side logic. However, it also comes with limitations: you won’t be able to use Astro’s built-in features like partial hydration or server-side rendering, lose file-based routing and other Astro-specific features. But if you’re looking for a way to inject dynamic content into your Astro projects, this is a good starting point.



