DEV Community

Cover image for πŸ”₯ Big update: the Gowebly CLI now supports Templ
Vic ShΓ³stak
Vic ShΓ³stak

Posted on • Updated on

πŸ”₯ Big update: the Gowebly CLI now supports Templ

Introduction

Hello, everyone! πŸ™Œ

I've released a big update to the Gowebly CLI project in v1.5.0 which includes Templ support for built-in net/http and all Go web frameworks: Fiber, go-chi and echo.

But let's not get ahead of ourselves, let's take a closer look.

πŸ“ Table of contents

What is Templ?

Templ is a language for writing HTML user interfaces in Go.

GitHub logo a-h / templ

A language for writing HTML user interfaces in Go.

You can use it to write code like this, which will be compiled into native Go functions to generate HTML:

// hello.templ

package main

templ Hello(name string) {
  <div>Hello, { name }</div>
}

templ Greeting(person Person) {
  <div class="greeting">
    @Hello(person.Name)
  </div>
}
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ Note: A complete user guide is here: https://templ.guide/

Create components that render fragments of HTML and compose them to create screens, pages, documents, or apps.

TOP features:

  • Server-side rendering: Deploy as a serverless function, Docker container, or standard Go program.
  • Static rendering: Create static HTML files to deploy however you choose.
  • Compiled code: Components are compiled into performant Go code.
  • Use Go: Call any Go code, and use standard if, switch, and for statements.
  • No JavaScript: Does not require any client or server-side JavaScript.
  • Great developer experience: Ships with IDE autocompletion.

Yes, Templ has plugins for most popular IDEs:

πŸ’‘ Note: Settings and useful tips you can read here.

↑ Table of contents

How to use Templ with Gowebly CLI?

The first thing you should do is verify that you are using Gowebly CLI version v1.5.0 or higher.

Next, start creating the configuration file:

gowebly init
Enter fullscreen mode Exit fullscreen mode

The CLI will generate a .gowebly.yml file with the following config:

backend:
   module_name: project # (string) option can be any name of your Go module (for example, 'github.com/user/project')
   go_framework: default # (string) option can be one of the values: 'fiber', 'echo', 'chi', or 'default'
   template_engine: default # (string) option can be one of the values: 'templ', or 'default'
   port: 5000 # (int) option can be any port that is not taken up on your system
   timeout:
      read: 5 # (int) option can be any number of seconds, 5 is recommended
      write: 10 # (int) option can be any number of seconds, 10 is recommended

frontend:
   package_name: project # (string) option can be any name of your package.json (for example, 'project')
   css_framework: default # (string) option can be one of the values: 'tailwindcss', 'unocss', or 'default'
   runtime_environment: default # (string) option can be one of the values: 'bun', or 'default'
   htmx: latest # (string) option can be any existing version
   hyperscript: latest # (string) option can be any existing version
Enter fullscreen mode Exit fullscreen mode

Yes, you're right! ✨ There is a new option template_engine in the backend block, which can take (so far) two values:

  • default to create a new project without a template engine;
  • templ to create a new project using Templ;

Now, change the value of this setting to templ and start creating a project:

gowebly create
Enter fullscreen mode Exit fullscreen mode

And CLI created a new project with the following structure:

.
β”œβ”€β”€ assets
β”‚Β Β  └── styles.css
β”œβ”€β”€ static
β”‚Β Β  β”œβ”€β”€ favicons
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ apple-touch-icon.png
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ favicon.ico
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ favicon.png
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ favicon.svg
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ manifest-desktop-screenshot.jpeg
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ manifest-mobile-screenshot.jpeg
β”‚Β Β  β”‚Β Β  └── manifest-touch-icon.svg
β”‚Β Β  β”œβ”€β”€ images
β”‚Β Β  β”‚Β Β  └── logo.svg
β”‚Β Β  β”œβ”€β”€ htmx.min.js
β”‚Β Β  β”œβ”€β”€ hyperscript.min.js
β”‚Β Β  β”œβ”€β”€ manifest.json
β”‚Β Β  └── styles.css
β”œβ”€β”€ templates
β”‚Β Β  β”œβ”€β”€ pages
-Β Β  β”‚Β Β  β”œβ”€β”€ index.html
+Β Β  β”‚Β Β  β”œβ”€β”€ index.templ
+Β Β  β”‚Β Β  └── index_templ.go
-Β Β  β”œβ”€β”€ main.html
+Β Β  β”œβ”€β”€ main.templ
+Β Β  └── main_templ.go
β”œβ”€β”€ go.mod
β”œβ”€β”€ go.sum
β”œβ”€β”€ handlers.go
β”œβ”€β”€ main.go
β”œβ”€β”€ package-lock.json
β”œβ”€β”€ package.json
└── server.go
Enter fullscreen mode Exit fullscreen mode

Files with the extension *.templ are our working files, and those with *_templ.go in the name are automatically generated for us with Templ.

❗️ Attention: Do not edit the *_templ.go Go files in any way. They will be generated automatically. Work only with *.templ files.

↑ Table of contents

Templates content

Okay, now let's take a look at the template files.

The ./templates/main.templ template contains the basic layout for the web application and can be modified as you wish:

// ./templates/main.templ

package templates

import "project/templates/pages"

// Define a secure policy for Content-Security-Policy header.
var metaContentSecurePolicy = "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' https://unpkg.com 'unsafe-inline' 'unsafe-eval';"

templ Layout(title string, metaTags, bodyContent templ.Component) {
    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8"/>
            <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
            <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
            <meta http-equiv="Content-Security-Policy" content={ metaContentSecurePolicy }/>
            <meta name="theme-color" content="#FEFEF5"/>
            <title>{ title }</title>
            {! metaTags }
            <link rel="manifest" href="/static/manifest.json"/>
            <link rel="apple-touch-icon" href="/static/favicons/apple-touch-icon.png"/>
            <link rel="shortcut icon" href="/static/favicons/favicon.ico" type="image/x-icon"/>
            <link rel="icon" href="/static/favicons/favicon.svg" type="image/svg+xml"/>
            <link rel="icon" href="/static/favicons/favicon.png" sizes="any"/>
            <link href="/static/styles.css" rel="stylesheet"/>
        </head>
        <body onload={ pages.BodyScripts() }>
            {! bodyContent }
            <script src="/static/htmx.min.js"></script>
            <script src="/static/hyperscript.min.js"></script>
        </body>
    </html>
}
Enter fullscreen mode Exit fullscreen mode

And the ./templates/pages/index.templ template will contain the content of the home page (accessible at /):

// ./templates/pages/index.templ

package pages

// MetaTags defines meta tags.

templ MetaTags(keywords, description string) {
    <meta name="keywords" content={ keywords }/>
    <meta name="description" content={ description }/>
}

// styledTextStyles defines CSS styles for component.

css styledTextStyles() {
    color: blue;
    margin: 1rem 0;
}

// BodyContent defines HTML content.

templ BodyContent(h1, text string) {
    <main>
        <div>
            <p>
                <img width="224px" src="/static/images/logo.svg" alt="logo"/>
            </p>
            <div>
                <h1>{ h1 }</h1>
                <p>{ text }</p>
                <p class={ styledTextStyles() }>
                    Hello from <strong>templ</strong>!
                    <br/>
                    Edit this styled text in the './templates/pages/index.templ' file.
                </p>
                <p>
                    <a href="https://gowebly.org" target="_blank">Documentation</a>,
                    <a href="https://github.com/gowebly/gowebly" target="_blank">GitHub</a>
                </p>
            </div>
            <div>
                <p><button hx-get="/api/show" hx-target="#htmx-result">Show content</button></p>
                <div id="htmx-result"></div>
            </div>
        </div>
    </main>
}

// BodyScripts defines JavaScript code.

script BodyScripts() {
    console.log("Hello from templ!", "Edit this JavaScript code in the './templates/pages/index.templ' file.");
}
Enter fullscreen mode Exit fullscreen mode

The code in the templates is as readable and easy to understand as possible for any developer who has ever made sites in HTML and understands Go.

↑ Table of contents

Backend handler changes

If it is clear with the templates themselves, here are the changes that handlers.go has undergone:

// handlers.go

// ...

// indexViewHandler handles a view for the index page.
func indexViewHandler(w http.ResponseWriter, r *http.Request) {
    // Check, if the current URL is '/'.
    if r.URL.Path != "/" {
        // If not, return HTTP 404 error.
        http.NotFound(w, r)
        slog.Error("render page", "method", r.Method, "status", http.StatusNotFound, "path", r.URL.Path)
        return
    }

    // Define template functions.
    metaTags := pages.MetaTags(
        "gowebly, htmx example page, go with htmx",               // define meta keywords
        "Welcome to example! You're here because it worked out.", // define meta description
    )
    bodyContent := pages.BodyContent(
        "Welcome to example!",                // define h1 text
        "You're here because it worked out.", // define p text
    )

    // Define template layout.
    if err := templates.Layout(
        "Welcome to example!", // define title text
        metaTags, bodyContent,
    ).Render(r.Context(), w); err != nil {
        // Send HTTP 500 error with log.
        w.WriteHeader(http.StatusInternalServerError)
        slog.Error("render template", "method", r.Method, "status", http.StatusInternalServerError, "path", r.URL.Path)
        return
    }

    // Send log message.
    slog.Info("render page", "method", r.Method, "status", http.StatusOK, "path", r.URL.Path)
}

// ...
Enter fullscreen mode Exit fullscreen mode

The templates.Layout() Go function is the Templ function from the ./templates/main.templ template, which was kindly generated automatically when the project was created.

πŸ’‘ Note: The Gowebly CLI knows that your project contains a Templ templer and will generate code from the *.templ templates every time it changes (hot-reload).

Let's run your project:

gowebly run
Enter fullscreen mode Exit fullscreen mode

gowebly screenshot

Yeah, it's just working! πŸŽ‰

↑ Table of contents

Photos and videos by

P.S.

If you want more articles (like this) on this blog, then post a comment below and subscribe to me. Thanks! 😻

And of course, you can help me make developers' lives even better! Just connect to one of my projects as a contributor. It's easy!

My projects that need your help (and stars) πŸ‘‡

  • πŸ”₯ gowebly: A next-generation CLI tool for easily build amazing web applications with Go on the backend, using htmx & hyperscript and the most popular atomic/utility-first CSS frameworks on the frontend.
  • ✨ create-go-app: Create a new production-ready project with Go backend, frontend and deploy automation by running one CLI command.
  • πŸƒ yatr: Yet Another Task Runner allows you to organize and automate your routine operations that you normally do in Makefile (or else) for each project.
  • πŸ“š gosl: The Go Snippet Library provides snippets collection for working with routine operations in your Go programs with a super user-friendly API and the most efficient performance.
  • πŸ„β€β™‚οΈ csv2api: The parser reads the CSV file with the raw data, filters the records, identifies fields to be changed, and sends a request to update the data to the specified endpoint of your REST API.
  • 🚴 json2csv: The parser can read given folder with JSON files, filtering and qualifying input data with intent & stop words dictionaries and save results to CSV files by given chunk size.

Top comments (1)

Collapse
 
mariusty profile image
mariusty

Thanks for the detailed examples! You rock, man!