Modern Web Development with Go: A Lightweight Alternative to React SSR

Sami Salih İbrahimbaş
5 min readDec 28, 2024

--

In recent years, the web development landscape has significantly evolved, especially with React’s server-side rendering (SSR) capabilities. However, for developers using non-JavaScript backend languages like Go, this presents a unique challenge. This article explores a modern, efficient approach to web development that combines the best of both worlds.

Story Cover

The Challenge with React SSR

React’s server components are revolutionary but come with a caveat: they require a Node.js runtime. For teams using Go or other non-JavaScript backends, this means:

  • Running separate servers for frontend and backend
  • Increased infrastructure complexity
  • Higher deployment costs
  • Limited serverless options
  • Larger deployment sizes

A Modern Alternative

After extensive research and experimentation, we’ve developed a lightweight stack that provides similar capabilities without the overhead:

  • Templ: For server-side rendering in Go
  • HTMX: For dynamic server interactions
  • Petite Vue (6kb): For client-side reactivity
  • Go’s embed: For static asset management

Why This Stack?

  1. Single Codebase: Everything lives in your Go application
  2. Minimal JavaScript: Only 6kb of client-side JS with Petite Vue
  3. Tiny Deployment Size: Around 19MB for a complete application
  4. Development Simplicity: Node.js needed only in development
  5. Enterprise-Ready: Perfect for companies prioritizing efficiency

How It Works

Architecture Overview

Our architecture is designed to be modular and efficient:

Architecture Overview

This architecture allows us to maintain a clean separation of concerns while keeping everything in a single, manageable codebase. The main application orchestrates different components, including REST APIs, gRPC services, and web applications, all sharing the same runtime.

Rendering Strategy

Our approach to rendering is optimized for both performance and developer experience:

Rendering Strategy

This rendering strategy provides:

- Full-page loads with server-side rendering for initial requests

- Partial updates through HTMX for dynamic content

- Lightweight client-side reactivity with Petite Vue

- Minimal JavaScript footprint (only 6kb)

1. Server-Side Rendering with Templ

// Dashboard component example
templ Dashboard(account models.Account) {
@Layout("Dashboard") {
@templ.JSONScript("account-data", account)

<div class="grid grid-cols-2 gap-6">
<div id="account-widget" v-cloak v-scope="accountWidget()" @vue:mounted="init">
<div class="bg-white rounded-lg shadow-lg p-6" v-if="account">
<h3 class="text-xl font-semibold text-gray-900 mb-4">Account Balance</h3>
<div class="text-3xl font-bold text-gray-900 mb-6">{{ formatAmount(account.balance) }}</div>
// ... template continues ...
</div>
</div>
</div>
}
}

2. Dynamic Interactions with HTMX and Petite Vue Combined

<div id="transaction-list" v-cloak v-scope="transactionList()" @vue:mounted="init">
<div class="bg-white rounded-lg shadow-lg p-6">
<div v-if="loading" class="py-3 text-gray-600">
Loading...
</div>
<div v-else class="border-t border-gray-200 pt-4">
<div v-for="tx in transactions" class="flex justify-between items-center py-3">
<span class="text-gray-600">{{ tx.description }}</span>
<span :class="['font-medium', tx.amount < 0 ? 'text-red-600' : 'text-green-600']">
{{ formatAmount(tx.amount) }}
</span>
</div>
</div>
</div>
</div>

Benefits for Enterprise

1. Cost Efficiency

  • Single server deployment
  • Reduced infrastructure complexity
  • Lower maintenance overhead

2. Performance

  • Minimal client-side JavaScript
  • Efficient server-side rendering
  • Small deployment size (≈19MB)

3. Developer Experience

  • Single language (Go) for backend logic
  • Clear separation of concerns
  • Familiar templating patterns

4. Security

  • Reduced attack surface
  • Server-side validation
  • Built-in CSRF protection

When to Use This Approach?

This stack is particularly suitable for:

  • Enterprise applications requiring high performance
  • Teams with Go expertise
  • Projects needing quick time-to-market
  • Applications with security constraints
  • Systems requiring minimal deployment footprint

Getting Started

The project structure follows a clean, modular approach:

webstack/
├── api/
│ ├── web/ # Web application
│ │ ├── handlers/ # HTTP handlers
│ │ ├── templates/ # Templ templates
│ │ └── static/ # Static assets
│ ├── rest/ # REST API endpoints
│ └── rpc/ # gRPC services
├── internal/ # Internal packages
└── cmd/ # Application entrypoints

Development and Deployment

Development Environment

We use Docker for development to ensure consistency. Here’s our development Dockerfile:

FROM golang:1.23-alpine

WORKDIR /app

COPY go.mod go.sum ./

RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/go/pkg/sumdb \
go mod download

COPY . .

RUN go install github.com/a-h/templ/cmd/templ@latest
RUN go install github.com/air-verse/air@latest

CMD ["air", "-c", ".air.toml"]

Production Deployment

Our production deployment uses a multi-stage build process:

  1. Node.js Stage: Builds frontend assets
  2. Go Build Stage: Compiles the application
  3. Final Stage: Creates a minimal production image
# Node Stage
FROM node:20.11.1-slim AS node_builder

WORKDIR /

COPY ./api/web ./

RUN npm install

RUN npm run build

# Go Stage
FROM golang:1.23-alpine AS builder
RUN apk update && apk add --no-cache ca-certificates

ENV CGO_ENABLED=0 GO111MODULE=on GOOS=linux

WORKDIR /

COPY go.* ./
RUN --mount=type=cache,target=/go/pkg/mod \
go mod download
COPY . .

# install templ
RUN go install github.com/a-h/templ/cmd/templ@latest

RUN templ generate ./api/web/templates/

COPY ./api/web/templates ./api/web/templates

RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go build -o main ./cmd/main.go

# Final Stage
FROM scratch

ENV HTTP_PORT=8080
ENV RPC_PORT=9090

COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /main .
COPY --from=node_builder /static/dist /api/web/static/dist
COPY --from=node_builder /templates /api/web/templates
EXPOSE $HTTP_PORT $RPC_PORT

CMD ["/main"]

This approach results in a tiny production image (≈19MB) containing only the necessary components for running the application.

Conclusion

This modern approach to web development offers a compelling alternative to React SSR for Go-based teams. It provides all the benefits of modern web development — reactivity, dynamic updates, and server-side rendering — while maintaining simplicity and efficiency.

The combination of Templ, HTMX, and Petite Vue creates a powerful, lightweight stack that’s particularly well-suited for enterprise applications where performance, security, and maintainability are crucial.

By embracing this approach, teams can build robust web applications with a fraction of the complexity and resource overhead typically associated with modern web development.

--

--

Sami Salih İbrahimbaş
Sami Salih İbrahimbaş

Written by Sami Salih İbrahimbaş

lifetime junior • software developer at monopayments

No responses yet