Intro
Ever wonder if you could build modern web apps like those built with Next.js, but using your favorite programming language? I did. In this article, I share how I created a Next.js-like framework for Go developers, powered by AWS.
Hi, I’m Felipe, a Serverless Expert specialized in Lambda containers and custom Lambda environments. For the past 6 years, I’ve helped many clients make smarter architectural decisions and build innovative tools using serverless infrastructure — like the framework you’ll discover in this article.
The Journey
It all started a few years ago when I began learning a new programming language — Golang. I quickly became enchanted by its simplicity and powerful performance. As a compiled language with strong support for multithreading and parallelism, it’s perfect for optimizing performance-critical tasks on custom Lambda environments and Lambda containers.
However, one thing disappointed me: at the time, there wasn’t an easy, well-built solution with all the bells and whistles to create end-to-end apps using Go. I’ve always enjoyed building full end-to-end solutions on AWS serverless infrastructure with JavaScript. Using Lambda functions combined with serverless frontends like Next.js gave me a powerful toolkit that made solving complex problems straightforward.
Despite my enthusiasm for Golang and its many strengths, the lack of a solid full-stack solution meant I had to keep using JavaScript as my primary language for personal projects and innovative tools.
HTMX and the GOTH Stack
Everything changed in 2024 when I first heard about HTMX and a new Go frontend stack gaining traction called the GOTH stack. GOTH stands for Golang, TailwindCSS, Templ, and HTMX.
I learned about it from a well-known former Netflix engineer, and later from other respected developers discussing how we might be relying too heavily on JavaScript for everything. Their point was not to stop using JavaScript, but to use it as it was originally intended — to handle frontend events.
According to them, every React package import drags along a ton of extra and often duplicated code inside node_modules
, which results in bloated JavaScript bundles. We then try to reduce these bundles with tools like Webpack, esbuild, and later Vite. Plus, we end up duplicating backend state on the client side, managing that cached state with complex libraries like Redux. Maybe we’re heading in the wrong direction. Maybe we should step back and rethink where responsibilities belong.
The backend should manage state — except for UI-related states tied directly to the page, like whether a menu is open.
This resonated deeply with me. I’d faced these issues before: wrestling with massive frontend states in Redux, or struggling to optimize large JavaScript bundles for SEO on pages that only needed a few JS components.
So, I decided to dive deeper and explore what HTMX and the GOTH stack were all about.
How HTMX Changed the Game
HTMX is a lightweight JavaScript library that lets you create dynamic, modern user interfaces with minimal client-side JavaScript. Instead of shipping tons of JS bundles, HTMX enables your HTML to send HTTP requests and update parts of the page directly — meaning less client-side state management and more server-driven logic.
This approach felt like a breath of fresh air after years of complex frontend frameworks. With HTMX, the frontend becomes a thin layer that communicates with the backend over AJAX-like requests, drastically reducing bundle size and complexity.
Pairing HTMX with Go’s speed and simplicity seemed like the perfect recipe to build modern, efficient web apps without the usual JavaScript overhead.
Introducing GOTH
The GOTH stack leverages the power of HTMX, TailwindCSS, Templ, and Golang to create an elegant, server-driven approach to web development. By combining these tools, we're able to build modern web apps with minimal client-side JavaScript, keeping everything simple and efficient.
TailwindCSS for Styling
Once HTMX has empowered your HTML to dynamically change parts of the page with minimal JavaScript, we need a powerful, modern way to style these dynamic components. Enter TailwindCSS—a utility-first CSS framework that's gained massive popularity for its simplicity and flexibility.
With Tailwind, you can quickly build beautiful, responsive designs without having to leave your HTML. It’s a perfect companion to HTMX because it allows you to style dynamic components as you go, all without the overhead of bundling complex CSS files or using CSS-in-JS solutions.
Templ: A Go Developer's JSX Alternative
The third pillar of the GOTH stack is Templ, a Go-based template engine that uses a syntax similar to React. This makes it incredibly intuitive for developers coming from JavaScript, as it allows you to create dynamic templates with props and child components. Templ lets you define your HTML templates in Go code, with logic and dynamic data injected directly into the HTML, similar to how React works with JSX.
For instance, with Templ, you can define your page layout in one file, and then import and reuse smaller components in a way that keeps your codebase clean and modular. You can even pass props and manage dynamic content directly inside your templates.
Example of Templ HTML page
Here’s an example of a simple Templ page that demonstrates how you can pass props and manage dynamic content, making it similar to how React components work.
Let’s walk through a simple example of using Templ to create a dynamic HTML page in Go. This will demonstrate how to pass data (props) to the page and how it behaves similarly to React components.
Defining the PageProps Structure
To start, we define a Go struct, PageProps
, that will hold the data we want to pass to the page. In this case, we’ll pass a simple message.
The Templ Page
Next, let’s define a simple Templ page that renders an HTML structure. This page will receive the PageProps
struct and display the message passed into it.
package pages
type PageProps struct{
Message string
}
templ TemplPageExample(props PageProps) {
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<title>Example Templ Page</title>
// Tailwind css output file
<link rel="stylesheet" href="/public/styles.css"/>
// Importing HTMX for handeling page events
<script src="https://unpkg.com/htmx.org@2.0.3" integrity="sha384-0895/pl2MU10Hqc6jd4RvrthNlDiE9U1tWmX7WRESftEDRosgxNsQG/Ze9YMRzHq" crossorigin="anonymous"></script>
</head>
// You can add tailwind classes to edit style of your page
<body class="antialiased flex flex-col justify-center items-center w-screen h-screen bg-black p-3">
<h1>Hello from Templ page!</h1>
<p>This is the message you passed as page props {props.Message}</p>
</body>
</html>
}
In the code above:
- We're using TailwindCSS to style the page, giving it a modern, responsive layout with minimal effort.
- The HTMX script is imported to handle any dynamic interactions you might want to add later, though for this simple example, we’re focusing on static content rendered from the server.
- The message from
PageProps
is displayed in the<p>
tag. Notice how we’re injectingprops.Message
directly into the template.
Now, this page is ready to be served by our Go web server!
Example of an HTTP Route Serving the Templ HTML Page
After generating the Go code from your .templ
file using the templ generate
command, you can serve the HTML page from an HTTP route. Below is an example of how to do that in a Go server.
First, make sure you’ve imported the generated Templ code into your Go server file.
Then, define a route (e.g., GET /example-templ
) to serve the Templ page with dynamic content.
router.Get("/example-templ", func(w http.ResponseWriter, r *http.Request) {
pages.TemplPageExample(pages.PageProps{Message: "Hello World"}).Render(r.Context(), w)
})
In the code above:
- We're using the Chi Mux router (or you could use the
net/http
router, depending on your preference) to handle HTTP requests. - The route
/example-templ
serves the Templ-generated HTML page. We pass theMessage
prop asHello World
, and when a user visits this URL, they’ll see the message rendered in the HTML.
How the Server and Template Work Together
- Server Setup: The server listens on
localhost:8080
. When the/example-templ
route is accessed, it renders the Templ page. - Template Rendering: The
pages.TemplPageExample
function receives thePageProps
struct, injects the dynamicMessage
prop into the template, and renders the final HTML page. - Result: When a user navigates to
http://localhost:8080/example-templ
, they’ll see the rendered HTML page with the message"Hello World"
passed in as a prop.
Final Thoughts on the GOTH Stack
While the GOTH stack allowed me to build full-stack apps with Go, I quickly encountered limitations that made the development experience less than ideal. While the stack provided a solid foundation, there were still several manual steps in the workflow that slowed down the development process.
For example, every time I made a change to a page, I had to stop the server, run the templ generate
command, and restart the server to see the updates. Additionally, for each new page or HTMX component, I had to manually create an HTTP route, which felt repetitive and tedious. While these steps were manageable for smaller projects, they became a bottleneck for larger applications.
Some well-known Go engineers had come up with a solution to make this process smoother by running TailwindCSS and Templ in watch mode, alongside using Go Air for hot reloading. This allowed the app to automatically reload when changes were made, saving time during development. However, this still felt like a workaround rather than a complete, elegant solution.
I realized I needed something even more streamlined—something that would make the entire development and deployment process simpler, faster, and more integrated. I wanted a solution that would eliminate these manual steps and allow me to focus on building features instead of managing workflows.
What I envisioned was a framework that could seamlessly integrate with AWS's fully managed serverless infrastructure — something as easy to deploy as Next.js with AWS Amplify. But at the time, no such solution existed for Go developers.
That’s when the idea for Gothic Framework was born. I wanted to create a tool that would simplify the development and deployment of full-stack Go apps, combining the best aspects of the GOTH stack with modern serverless practices. Gothic Framework automates the repetitive steps and streamlines the process, allowing developers to build and deploy Go applications quickly and effortlessly, all while maintaining performance, flexibility, and simplicity.
With Gothic Framework, I aimed to create an all-in-one solution that empowered Go developers to focus on building great apps, without the overhead of complex workflows or deployment challenges. It’s a framework built for the modern web, designed to make Go development as enjoyable and productive as possible.
The Evolution of Gothic Framework: From Scripts to Full-Stack Deployment
Gothic Framework started as a collection of scripts tied together to help build, develop, and later deploy serverless infrastructure to AWS using AWS SAM (Serverless Application Model). The initial idea was to recreate the seamless developer experience I loved in Next.js, but for Go developers.
Recreating Next.js Features
I wanted to capture the simplicity and efficiency of Next.js, particularly its dynamic features like image optimization and link prefetching. So, I began designing scripts to replicate some of these features, such as the Next.js Image Component or the Next.js Link Component. These components, for example, would prefetch linked pages on hover, just as in Next.js.
Once I had these components—and several other supporting scripts—working smoothly in the MVP 1.0.0 version of Gothic Framework, I shifted my focus to infrastructure. I crafted a SAM YAML template to automatically provision all the necessary AWS resources.
Infrastructure Architecture
At this point, the infrastructure for Gothic Framework had evolved into something like the following:

The infrastructure includes:
- CloudFront Distribution: For caching pages and public assets at the edge.
- Lightweight Lambda Container: Running the Go framework server. The server runs in a Lambda URL container environment, which is extremely lightweight and optimized for speed.
- Origin Access ID: Protecting access from CloudFront to our Lambda function.
Optimizing Performance
Since Go is a compiled language, the final container image could use the scratch image to keep the final size very small—around 4MB. This minimal size contributed to incredibly fast cold starts, with an average response time of just 40 milliseconds—which is quite impressive for serverless environments.
One of the most exciting aspects of this architecture was integrating CloudFront with Cache-Control Headers. By sending appropriate cache headers from the Lambda functions, Gothic Framework was able to recreate the highly praised Incremental Static Regeneration (ISR) functionality seen in Next.js.
What this meant was that, just like in Next.js, users would receive cached content on their first request, and while they interact with the cached content, a new version would be generated in the background when the TTL (Time-To-Live) expired. CloudFront can cache content for up to a year, making this system extremely efficient and scalable.
Version 2.0.0: Combining Tools into a Single Framework
After testing the infrastructure and the deployment scripts, I bundled everything into a single Go binary. This version included:
- CLI Commands for creating new projects.
- Hot Reloading for existing projects.
- Auto-Route Generation based on file structure—just like Next.js does. This would automatically create routes as you added files to the project, dramatically simplifying the development process.
- AWS SAM Integration: To easily deploy this serverless infrastructure to AWS directly from the command line.
With all these features working together, Gothic Framework 2.0.0 was released and is the current version of the framework as of June 2025.
If you're curious to explore the current version or dive into the code, you can check out my GitHub repository.
Quick Hands-On with Gothic Framework
Now that you’ve heard the story behind Gothic Framework, let’s create and deploy a simple app to AWS so you can see it in action.
🧰 Prerequisites
Before continuing, make sure the following tools are installed and configured:
- Golang installed
- GOPATH properly configured (needed to use imported libraries)
- AWS CLI configured (
aws configure
) - AWS SAM CLI installed
- Make installed
Import the Framework
Once you have all prerequisites in place, install the latest version of Gothic Framework:
go install github.com/felipegenef/gothicframework@latest
Initialize a Project
Create a folder for your new project, then run:
gothicframework init
This command will prompt you for:
- A project name (in kebab-case)
- A Go module name
If you’re unsure what to enter, use something like:
my-first-goth-project
github.com/yourusername/myFirstGothProject
After that, the CLI will scaffold your project. You’ll see several files and folders generated—let’s get it running!
Hot Reload & Local Development
To run your app locally, use:
make dev
or:
gothicframework hot-reload
Your app will be available at http://localhost:3000. If your browser doesn’t open automatically, just paste the address into your browser manually.
You should see a welcome screen like this:

Want to test the hot reloading? Try editing Tailwind classes or content in /src/pages/index.templ
— as soon as you save, your changes will reflect instantly in the browser. Magic!
Creating a New Page (File-Based Routing)
Let’s add a new page to test file-based routing.
Create a file called test.templ
inside /src/pages/
with the following content:
package pages
import (
"github.com/yourusername/myFirstGothProject/src/layouts"
routes "github.com/felipegenef/gothicframework/pkg/helpers/routes"
"net/http"
)
type TestPageProps = interface{}
var TestPageConfig = routes.RouteConfig[TestPageProps]{
Type: routes.STATIC,
HttpMethod: routes.GET,
Middleware: func(w http.ResponseWriter, r *http.Request) TestPageProps {
return nil
},
}
templ Test(props TestPageProps) {
@layouts.PageLayout() {
<h2 class="text-4xl text-white">Hello from my first Test page!</h2>
}
}
After saving, go to http://localhost:3000/test — your new page should be live:
✅ Tip: Gothic automatically generates routes based on the files inside /src/pages, /src/components, and /src/api. After saving a file, it updates the /src/routes/autoGenRoutes.go file. It's just like how Next.js handles file-based routing!

Deploying Your First Gothic App
Alright, time to deploy to AWS!
First, add a deploy
configuration to your gothic-config.json
:
{
"projectName": "your-project-name",
"goModuleName": "your-go-module-name",
"optimizeImages": {
"lowResolutionRate": 20
},
"deploy": {
"serverMemory": 128,
"serverTimeout": 30,
"region": "us-east-1",
"customDomain": false,
"profile": "default",
"stages": {
"dev": {
"hostedZoneId": null,
"customDomain": null,
"certificateArn": null,
"env": {}
},
"staging": {
"hostedZoneId": null,
"customDomain": null,
"certificateArn": null,
"env": {}
},
"prod": {
"hostedZoneId": null,
"customDomain": null,
"certificateArn": null,
"env": {}
}
}
}
}
Then deploy using either:
make deploy STAGE=dev
or
gothicframework deploy --action deploy --stage dev
After a few minutes, AWS SAM will finish deploying your app. The terminal will show a CloudFormation URL — open it to see your app live on AWS! 🎉
Cleanup
To delete your deployed app from AWS:
make delete STAGE=dev
or
gothicframework deploy --action delete --stage dev
Conclusion
Pretty cool, right?! 😄
If you’d like to see everything Gothic Framework can do, visit the official documentation and give it a ⭐ on GitHub!
After building this and using it in several pet projects, I think I’ve found my favorite stack. Gothic Framework brings many of the benefits of Next.js, but it’s written in Golang—my language of choice.
This was my journey building a modern framework for Go.
Now I’m curious: what tools have you built for your favorite language?
Do you have a project you can’t stop reusing in every app? Let me know!
Research/References
HTMX
: https://htmx.org/TEMPL
: https://templ.guide/TAILWIND
: https://tailwindcss.com/AWS SAM
: https://aws.amazon.com/serverless/sam/AIR
: https://github.com/air-verse/airGOTHIC FRAMEWORK
: https://gothicframework.com/