A strongly typed HTML templating language that compiles to Go code, and has great developer tooling.

Overview

templ

  • A strongly typed HTML templating language that compiles to Go code, and has great developer tooling.

vscode-autocomplete

Getting started

  • Install the templ command-line tool: go install github.com/a-h/templ/cmd/[email protected]
  • Create a *.templ file containing a template.
  • Run templ generate to create Go code from the template.

Current state

This is beta software, and the template language may still have breaking changes. There's no guarantees of stability or correctness at the moment.

If you're keen to see Go be practical for Web projects, see "Help needed" for where the project needs your help.

Features

The language generates Go code, some sections of the template (e.g. package, import, if, for and switch statements) are output directly as Go expressions in the generated output, while HTML elements are converted to Go code that renders their output.

  • templ generate generates Go code from *.templ files.
  • templ fmt formats template files in the current directory tree.
  • templ lsp provides a Language Server to support IDE integrations. The compile command generates a sourcemap which maps from the *.templ files to the compiled Go file. This enables the templ LSP to use the Go language gopls language server as is, providing a thin shim to do the source remapping. This is used to provide autocomplete for template variables and functions.

Security

templ currently uses context unaware escaping, see https://github.com/a-h/templ/issues/6 for a proposal to add context-aware content escaping.

Design

Overview

  • *.templ files are used to generate Go code to efficiently render the template at runtime.
  • Go code is generated from template files using the templ generate command, while templates can be formatted with the templ fmt command. The templ lsp command provides an LSP (Language Server Protocol) server to enable autocomplete.
  • Each {% templ ComponentName(params Params) %} section compiles into a function that creates a templ.Component.
  • templ.Component is an interface with a single function - Render(ctx context.Context, io.Writer) (err error). You can make a component entirely in Go code and interact with it via templ.
  • templ aims for correctness, simplicity, developer experience and raw performance, in that order. The goal is to make writing Go web applications more practical, achievable and desirable.
  • Provides minified HTML output only.
  • Components can be composed into layouts.

Package

Since templ files are as close to Go as possible, they start with a package expression.

{% package templ %}

Importing packages

After the package expression, they might import other Go packages, just like Go files. There's no multi-line import statement, just a single import per line.

{% import "strings" %}

Components

Once the package and import statements are done, we can define components using the {% templ Name(params Params) %} expression. The templ expressions are converted into Go functions when the templ generate command is executed.

{% templ AddressView(addr Address) %}
	
{%= addr.Address1 %}
{%= addr.Address2 %}
{%= addr.Address3 %}
{%= addr.Address4 %}
{% endtempl %}

Each templ.Component can contain HTML elements, strings, for loops, switch statements and references to other templates.

Referencing other components

Components can be referenced in the body of the template, and can pass data between then, for example, using the AddressTemplate from the PersonTemplate.

{% templ PersonTemplate(p Person) %}
	
{% for _, v := range p.Addresses %} {%! AddressTemplate(v) %} {% endfor %}
{% endtempl %}

It's also possible to create "higher order components" that compose other instances of templ.Component without passing data, or even knowing what the concrete type of the component will be ahead of time. So long as is implements templ.Component, it can be used.

For example, this template accepts 3 templates (header, footer, body) and renders all 3 of them in the expected order.

{% templ Layout(header, footer, body templ.Component) %}
	{%! header %}
	{%! body %}
	{%! footer %}
{% endtempl %}

Code-only components

It's possible to create a templ.Component entirely in Go code. Within templ, strings are automatically escaped to reduce the risk of cross-site-scripting attacks, but it's possible to create your own "Raw" component that bypasses this behaviour:

func Raw(s string) templ.Component {
	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
		_, err = io.WriteString(w, s)
		return
	})
}

Then call it in a template. So long as the Raw function is in scope, you can use it.

alert('xss vector');") %} ">
{%! Raw("") %}

Elements

HTML elements look like HTML and you can write static attributes into them, just like with normal HTML. Don't worry about the spacing, the HTML will be minified when it's rendered.

{%= addr.Address1 %}
">
{%= addr.Address1 %}

You can also have dynamic attributes that use template parameter, other Go variables that happen to be in scope, or call Go functions that return a string. Don't worry about HTML encoding element text and attribute values, that will be taken care of automatically.

Text

Text is rendered from Go expressions, which includes constant values:

{%= "this is a string" %}

Using the backtick format (single-line only):

{%= `this is also a string` %}

Calling a function that returns a string:

{%= time.Now().String() %}

Or using a string parameter, or variable that's in scope.

{%= v.s %}

What you can't do, is write text directly between elements (e.g.

Some text
, because the parser would have to become more complex to support HTML entities and the various mistakes people make when they're doing that (bare ampersands etc.). Go strings support UTF-8 which is much easier, and the escaping rules are well known by Go programmers.

If/Else

Templates can contain if/else statements that follow the same pattern as Go.

{%= "Test user" %} {% else %} {%= "Not test user" %} {% endif %} ">
{% if p.Type == "test" %}
	{%= "Test user" %}
{% else %}
	{%= "Not test user" %}
{% endif %}

For

Templates have the same loop behaviour as Go.

{% for _, v := range p.Addresses %}
	
  • {%= v.City %}
  • {% endfor %}

    Switch/Case

    Switch statements work in the same way as they do in Go.

    {%= "Test user" %} {% endcase %} {% case "admin" %} {%= "Admin user" %} {% endcase %} {% default %} {%= "Unknown user" %} {% enddefault %} {% endswitch %} ">
    {% switch p.Type %}
    	{% case "test" %}
    		{%= "Test user" %}
    	{% endcase %}
    	{% case "admin" %}
    		{%= "Admin user" %}
    	{% endcase %}
    	{% default %}
    		{%= "Unknown user" %}
    	{% enddefault %}
    {% endswitch %}
    

    Full example

    {%= addr.Address1 %}
    {%= addr.Address2 %}
    {%= addr.Address3 %}
    {%= addr.Address4 %}
    {% endtempl %} {% templ PersonTemplate(p Person) %}
    {%= p.Name() %}
    {%= strings.ToUpper(p.Name()) %}
    {% if p.Type == "test" %} {%= "Test user" %} {% else %} {%= "Not test user" %} {% endif %} {% for _, v := range p.Addresses %} {%! AddressTemplate(v) %} {% endfor %} {% switch p.Type %} {% case "test" %} {%= "Test user" %} {% endcase %} {% case "admin" %} {%= "Admin user" %} {% endcase %} {% default %} {%= "Unknown user" %} {% enddefault %} {% endswitch %}
    {% endtempl %} ">
    {% package templ %}
    
    {% import "strings" %}
    
    {% templ Layout(header, footer, body templ.Component) %}
    	{%! header %}
    	{%! body %}
    	{%! footer %}
    {% endtempl %}
    
    {% templ AddressTemplate(addr Address) %}
    	
    {%= addr.Address1 %}
    {%= addr.Address2 %}
    {%= addr.Address3 %}
    {%= addr.Address4 %}
    {% endtempl %} {% templ PersonTemplate(p Person) %}
    {%= p.Name() %}
    {%= strings.ToUpper(p.Name()) %}
    {% if p.Type == "test" %} {%= "Test user" %} {% else %} {%= "Not test user" %} {% endif %} {% for _, v := range p.Addresses %} {%! AddressTemplate(v) %} {% endfor %} {% switch p.Type %} {% case "test" %} {%= "Test user" %} {% endcase %} {% case "admin" %} {%= "Admin user" %} {% endcase %} {% default %} {%= "Unknown user" %} {% enddefault %} {% endswitch %}
    {% endtempl %}

    Will compile to Go code similar to the following (error handling removed for brevity):

    ") _, err = io.WriteString(w, templ.EscapeString(addr.Address1)) _, err = io.WriteString(w, "
    ") _, err = io.WriteString(w, "
    ") _, err = io.WriteString(w, templ.EscapeString(addr.Address2)) _, err = io.WriteString(w, "
    ") // Cut for brevity. return err }) } func PersonTemplate(p Person) (t templ.Component) { return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { _, err = io.WriteString(w, "
    ") _, err = io.WriteString(w, "
    ") _, err = io.WriteString(w, templ.EscapeString(p.Name())) _, err = io.WriteString(w, "
    ") _, err = io.WriteString(w, "") _, err = io.WriteString(w, templ.EscapeString(strings.ToUpper(p.Name()))) _, err = io.WriteString(w, "") _, err = io.WriteString(w, "
    ") if p.Type == "test" { _, err = io.WriteString(w, "") _, err = io.WriteString(w, templ.EscapeString("Test user")) _, err = io.WriteString(w, "") } else { _, err = io.WriteString(w, "") _, err = io.WriteString(w, templ.EscapeString("Not test user")) _, err = io.WriteString(w, "") } for _, v := range p.Addresses { err = AddressTemplate(v).Render(ctx, w) } switch p.Type { case "test": _, err = io.WriteString(w, "") _, err = io.WriteString(w, templ.EscapeString("Test user")) _, err = io.WriteString(w, "") case "admin": _, err = io.WriteString(w, "") _, err = io.WriteString(w, templ.EscapeString("Admin user")) _, err = io.WriteString(w, "") default: _, err = io.WriteString(w, "") _, err = io.WriteString(w, templ.EscapeString("Unknown user")) _, err = io.WriteString(w, "") } _, err = io.WriteString(w, "
    ") _, err = io.WriteString(w, "
    ") return err }) } ">
    // Code generated by templ DO NOT EDIT.
    
    package templ
    
    import "github.com/a-h/templ"
    import "context"
    import "io"
    import "strings"
    
    func Layout(header, footer, body templ.Component) (t templ.Component) {
    	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
    		err = header.Render(ctx, w)
    		err = body.Render(ctx, w)
    		err = footer.Render(ctx, w)
    		return err
    	})
    }
    
    func AddressTemplate(addr Address) (t templ.Component) {
    	return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
    		_, err = io.WriteString(w, "
    ") _, err = io.WriteString(w, templ.EscapeString(addr.Address1)) _, err = io.WriteString(w, "
    "
    ) _, err = io.WriteString(w, "
    ") _, err = io.WriteString(w, templ.EscapeString(addr.Address2)) _, err = io.WriteString(w, "
    "
    ) // Cut for brevity. return err }) } func PersonTemplate(p Person) (t templ.Component) { return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { _, err = io.WriteString(w, "
    ") _, err = io.WriteString(w, "
    ") _, err = io.WriteString(w, templ.EscapeString(p.Name())) _, err = io.WriteString(w, "
    "
    ) _, err = io.WriteString(w, ") _, err = io.WriteString(w, " href=") _, err = io.WriteString(w, "\"") _, err = io.WriteString(w, templ.EscapeString(p.URL)) _, err = io.WriteString(w, "\"") _, err = io.WriteString(w, ">") _, err = io.WriteString(w, templ.EscapeString(strings.ToUpper(p.Name()))) _, err = io.WriteString(w, "") _, err = io.WriteString(w, "
    ") if p.Type == "test" { _, err = io.WriteString(w, "") _, err = io.WriteString(w, templ.EscapeString("Test user")) _, err = io.WriteString(w, "") } else { _, err = io.WriteString(w, "") _, err = io.WriteString(w, templ.EscapeString("Not test user")) _, err = io.WriteString(w, "") } for _, v := range p.Addresses { err = AddressTemplate(v).Render(ctx, w) } switch p.Type { case "test": _, err = io.WriteString(w, "") _, err = io.WriteString(w, templ.EscapeString("Test user")) _, err = io.WriteString(w, "") case "admin": _, err = io.WriteString(w, "") _, err = io.WriteString(w, templ.EscapeString("Admin user")) _, err = io.WriteString(w, "") default: _, err = io.WriteString(w, "") _, err = io.WriteString(w, templ.EscapeString("Unknown user")) _, err = io.WriteString(w, "") } _, err = io.WriteString(w, "
    "
    ) _, err = io.WriteString(w, "
    "
    ) return err }) }

    IDE Support

    vim / neovim

    A vim / neovim plugin is available from https://github.com/Joe-Davidson1802/templ.vim which adds syntax highlighting.

    Neovim 5 supports Language Servers directly. For the moment, I'm using https://github.com/neoclide/coc.nvim to test the language server after using Joe-Davidson1802's plugin to set the language type:

    {
      "languageserver": {
        "templ": {
          "command": "templ",
          "args": ["lsp"],
          "filetypes": ["templ"]
        }
    }

    To add extensive debug information, you can include additional args to the LSP, like this:

    {
      "languageserver": {
        "templ": {
          "command": "templ",
          "args": ["lsp",
            "--log", "/Users/adrian/github.com/a-h/templ/cmd/templ/lspcmd/templ-log.txt", 
    	"--goplsLog", "/Users/adrian/github.com/a-h/templ/cmd/templ/lspcmd/gopls-log.txt",
    	"--goplsRPCTrace", "true"
          ],
          "filetypes": ["templ"]
        }
    }

    vscode

    There's a VS Code extension, just make sure you've already installed templ and that it's on your path.

    Development

    Local builds

    To build a local version you can use the go build tool:

    cd cmd/templ
    go build
    

    Testing

    Unit tests use the go test tool:

    go test ./...
    

    Release testing

    This project uses https://github.com/goreleaser/goreleaser to build the command line binary and deploy it to Github. You will need to install this to test releases.

    make build-snapshot
    

    The binaries are created by me and signed by my GPG key. You can verify with my key https://adrianhesketh.com/a-h.gpg

    Inspiration

    Doesn't this look like a lot like https://github.com/valyala/quicktemplate ?

    Yes, yes it does. I looked at the landscape of Go templating languages before I started writing code and my initial plan was to improve the IDE support of quicktemplate, see https://github.com/valyala/quicktemplate/issues/80

    The package author didn't respond (hey, we're all busy), and looking through the code, I realised that it would be hard to modify what's there to have the concept of source mapping, mostly because there's no internal object model of the language, it reads and emits code in one go.

    It's also a really feature rich project, with all sorts of formatters, and support for various languages (JSON etc.), so I borrowed some syntax ideas, but left the code. If valyala is up for it, I'd be happy to help integrate the ideas from here. I just want Go to have a templating language with great IDE support.

    Hot reload

    For hot reload, you can use https://github.com/cosmtrek/air

    For documentation on how to use it with templ see https://adrianhesketh.com/2021/05/28/templ-hot-reload-with-air/

    Help needed

    The project is looking for help with:

    • Adding features to the Language Server implementation, it just does autocomplete and error reporting the moment. It needs to be able to do definition and add imports automatically.
    • Examples and testing of the tools.
    • Writing a blog post that demonstrates using the tool to build a form-based Web application.
    • Testing (including fuzzing), benchmarking and optimisation.
    • An example of a web-based UI component library would be very useful, a more advanced version of the integration test suite, thatwould be a Go web server that runs the compiled templ file along with example JSON payloads that match the expected data structure types and renders the content - a UI playground. If it could do hot-reload, amazing.
    • Low priority, but I'm thinking of developing a CSS-in-Go implementation to work in parallel. This might take the form of a pre-processor which would collect all "style" attributes of elements and automatically calculate a minimum set of CSS classes that could be created and applied to the elements - but a first pass could just be a way to define CSS classes in Go to allow the use of CSS variables.

    Please get in touch if you're interested in building a feature as I don't want people to spend time on something that's already being worked on, or ends up being a waste of their time because it can't be integrated.

    Issues
    • proposal: change to templ to use single braces

      proposal: change to templ to use single braces

      In #35, there's a suggestion that the templ expression start and end token of {% and %} is unwieldy.

      Why did we end up with {% / %}?

      Initially, when choosing delimiters I was looking for something that was easy to type, reasonable looking, and not likely to show up in code.

      I considered double braces {{ but since the standard library, mustache and hugo all use these, and that double braces show up in Go code, I didn't think it would ideal.

      I ended up with the {% idea because it isn't valid Go code.

      What would be better?

      I think a single brace { would be best, because it's the smallest amount of typing, but thought that the complexity of the parser would be high, since I'd have to parse Go code to find the end of statements.

      How?

      However, I think I've got a solution to remove the need for {% and to drop it to {.

      The templ expressions only show up at particular points in the document (e.g. attribute values, within the main document), there's a strong expectation for the expression to be present, meaning that it's less important for the templ start/end token to be globally unique.

      If I use a brace instead, the parser can parse until the closing brace. Braces in Go code must be balanced, so any Go code will balance the brace count, however, the parser would need to ignore braces that are within string literals, since they may contain unbalanced braces.

      The basic algorithm is:

      • Start with an expression start e.g. { templ and set braceCount = 1
      • Read char
        • If " handle any escaped quotes or ticks until the end of the string.
        • If ``` read until the closing tick.
        • If { or }
          • If { do braceCount++
          • If } do braceCount--
          • If braceCount == 0, break
        • If EOL, break
        • If EOF, break
        • Default: add the char the expression, goto Read char
      • If braceCount != 0 throw an error

      I've written an implementation here which shows how expressions can be parsed.

      https://gist.github.com/a-h/c7c2a04afd5605c48496e5b346956455

      Difference between current and new code

      • Old: {% if findOut(func() bool { return true }) %}

      • New: { if findOut(func() bool { return true }) }

      • Old: {% endif %}

      • New: { endif }

      Since the only allowed expression here is a string expression, there's no need to specify the equals sign {%=, making it possible to simplify down by two chars.

      • Old: <a href={%= "https://google.com" %} />
      • New: <a href={ "https://google.com" } />

      The same applies here.

      • Old: <div>{%= theData() %}</div>
      • Old: <div>{ theData() }</div>

      However, loading a template would still need a specifier:

      • Old: <div>{%! header() %}</div>
      • New: <div>{! header() }</div>

      Backwards compatibility

      I don't think it's a good idea to break existing code, so I would:

      • Add a templ migrate function which does find and replace in templ files, replacing:
        • {% with {
        • {%= with {
        • {%! with {!
        • %} with }
      • Update templ generate and templ fmt - if they see the old pattern {% in the code, they would stop and print out a suggestion to run templ migrate, explain the reasoning for the change, and point to this issue.

      Ecosystem

      The templ vim plugin and VS Code plugin would need to be updated. I don't propose maintaining backwards compatibility for these, since the migration will be one way.

      Thoughts / feedback?

      Is this worth doing? Any concerns on this migration? @joerdav - I assume it's not a big job to migrate the vim plugin if I went ahead with this?

      opened by a-h 13
    • templ fmt: unexpected formatting result.

      templ fmt: unexpected formatting result.

      I have a template, and wanted to run templ fmt on it. I expected the various elements to be put onto more lines, however the opposite happened.

      Input template:

      {% templ form(action string, t todo, msg string) %}
              <div>
                      <p class={%= templ.Classes(invalid()) %}>{%= msg %}</p>
                      <form action={%= action %} method="post">
                          <span class={%= templ.Classes(cell()) %}><input type="text" value={%= t.name %} name="name"/></span>
                          <span class={%= templ.Classes(cell()) %}><input type="text" value={%= t.description %} name="description"/></span>
                          <span class={%= templ.Classes(cell()) %}>Done? <input type="checkbox" value="true" name="status" checked?={%= t.status %}/></span> 
                          <span class={%= templ.Classes(cell()) %}><input type="submit" value="Submit"/></span>
                      </form>
              </div>
      {% endtempl %}
      

      Expected:

      I'd expect it to either indent all elements or indent some of them depending on the length of them.

      Actual:

      {% templ form(action string, t todo, msg string) %}
              <div>
                      <p class={%= templ.Classes(invalid()) %}>{%= msg %}</p>
                      <form action={%= action %} method="post"><span class={%= templ.Classes(cell()) %}><input type="text" value={%= t.name %} name="name"/></span><span class={%= templ.Classes(cell()) %}><input type="text" value={%= t.description %} name="description"/></span><span class={%= templ.Classes(cell()) %}>Done? <input type="checkbox" value="true" name="status" checked?={%= t.status %}/></span><span class={%= templ.Classes(cell()) %}><input type="submit" value="Submit"/></span></form>
              </div>
      {% endtempl %}
      
      opened by joerdav 5
    • Improve install process

      Improve install process

      Hello,

      I'm trying to install templ, and I have a couple of suggestions to make it easier to install:

      Include an "Install" section in README.md

      I might have missed a section, but I didn't see how to install templ from the README, only how to build locally. It can be a really short section, but also really helpful :) If you'd like inspiration, I'm pretty happy with how my Install section turned out.

      Make templ easily go installable

      I was able to install templ with go install github.com/a-h/templ/cmd, but because the last directory is named cmd, the binary is now named cmd on my system. If you moved it to github.com/a-h/templ/cmd/templ, I would be able to run go install github.com/a-h/templ/cmd/templ and end up with a binary named templ

      Use GoReleaser's HomeBrew support

      GoReleaser can support generating Homebrew formulas so MacOS users can do something like brew install a-h/tap/templ if they wish. They won't even need GOPATH/bin in their PATH, because Homebrew will manage paths. I use this functionality for a little CLI I wrote and it works well once you get the tap repo and access token build- very much set up and forget. Feel free to take inspiration from my .goreleaser.yml

      opened by bbkane 5
    • Feature request: Introduce context-aware escaping

      Feature request: Introduce context-aware escaping

      templ currently uses simple escaping rules, described below:

      {% templ Example() %}
      <script type="text/javascript">
        {%= "will be escaped using templ.Escape, which isn't JavaScript-aware" %}
      </script>
      <div onClick={%= "will be escaped using templ.Escape, which isn't JavaScript aware" %}>
        {%= "will be escaped using templ.Escape, which is fine" %}</div>  
      </div>
      <style type="text/css">
        {%= "will be escaped using templ.Escape, which isn't CSS-aware" %}
      </style>
      <div style={%= "will be escaped using templ.Escape, which isn't CSS-aware" %}</div>
      <div>{%= "will be escaped using templ.Escape, which is fine" %}</div>
      <a href={%= "will be escaped using templ.Escape, which isn't hyperlink-aware" %}</div>
      {% endtempl %}
      

      This approach is safe when the content being applied to the onClick / on* / style, attributes and script / style element contents are from trusted sources, but if not, they could become attack vectors.

      In most cases, developers won't be adding hyperlinks, or populating onClick elements from user-controlled content, but since it's a potential attack vector, it's probably worth making developers explicitly state the safety, despite the extra developer hassle.

      templ's element type should be aware of href, on* and style attributes and the contents of script and style tags, and automatically apply context-aware escaping improve the security posture of templ.

      In cases where developers need to use content (css, scripts, hyperlinks) from data that's under their control (i.e. not potentially under an attacker's control), they'll need to state that the content is "safe".

      Language changes

      Option 1 - New string expression operator

      This could be done by adding a new context unaware string expression operator that only uses the templ.Escape function, rather than context-aware escaping. The syntax could be:

      • {%!=
      • {%= safe

      Option 2 - Force a function call to safehtml

      Make the CSS, onClick etc. take appropriate types from https://github.com/google/safehtml and force users to use safehtml.StyleFromConstant method calls.

      I think this would be irritating, because it's such a long function call.

      Option 3 - Wrap the function call to safehtml

      Similar, to above, but create a github.com/a-h/templ/safe package and shorten the calls:

      • {%= safe.Style("background-color: red") %}
      • {%= safe.Script("alert('hello')") %}

      Decision

      I don't like adding new templ expressions unless really required, so while I like option 1 (particularly the {%!= operator), I think option 3 is the way to go, because it's very clear what's going on even if you've never seen templ before, and there's not many places where developers will actually need to do this (onClick handlers, contents of <script> tags, style attributes).

      While it's potentially a breaking change for some users, we're not stable, so I think it's a good change.

      opened by a-h 5
    • DOCTYPE declaration causes strangeness in the vscode extension

      DOCTYPE declaration causes strangeness in the vscode extension

      When creating a .templ file I noticed a very strange thing happening if the layout had a doctype declaration.

      For example, this code (which looks like it should be valid to me):

      {% package templates %}
      
      {% templ Layout(content templ.Component) %}
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Document</title>
      </head>
      <body>
          {%! content %}
      </body>
      </html>
      {% endtempl %}
      
      {% templ Layout1(content templ.Component) %}
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Document</title>
      </head>
      <body>
          {%! content %}
      </body>
      </html>
      {% endtempl %}
      
      

      when this was saved in vscode, immediately turns into:

      {% package templates %}
      

      i.e. the actual templ declarations were removed.

      The actual layout was much more detailed but I distilled it down to the fact that if any templ declaration with a doctype declaration exists in the templ file then all templ declarations are removed, but only if the first templ declaration is the one that contains the doctype. Having the templ declarations the other way round (Layout1 first, followed by Layout) it works fine.

      I hope it's just me.

      Any ideas?

      opened by pharrisee 4
    • panic on Windows

      panic on Windows

      On Windows, templ stop working after starting. As far as I looked log file, it seems panic occurred.

      panic: runtime error: invalid memory address or nil pointer dereference
      [signal 0xc0000005 code=0x1 addr=0x40 pc=0x96b52c]
      
      goroutine 8 [running]:
      github.com/sourcegraph/jsonrpc2.(*Conn).send(0x0, {0xc00009c040, 0xc0000718a0}, 0xc000086330, 0x1)
      	C:/Users/mattn/go/pkg/mod/github.com/sourcegraph/[email protected]/jsonrpc2.go:352 +0x6c
      github.com/sourcegraph/jsonrpc2.(*Conn).DispatchCall(0x0, {0xa2b078, 0xc000028440}, {0xc0000882d0, 0xa}, {0x98ff20, 0xc00009c040}, {0x0, 0x0, 0x0})
      	C:/Users/mattn/go/pkg/mod/github.com/sourcegraph/[email protected]/jsonrpc2.go:449 +0x191
      github.com/sourcegraph/jsonrpc2.(*Conn).Call(0x98ff20, {0xa2b078, 0xc000028440}, {0xc0000882d0, 0xc000094020}, {0x98ff20, 0xc00009c040}, {0x990260, 0xc0000b2000}, {0x0, ...})
      	C:/Users/mattn/go/pkg/mod/github.com/sourcegraph/[email protected]/jsonrpc2.go:424 +0x72
      github.com/a-h/templ/cmd/templ/lspcmd.(*Proxy).proxyInitialize(0xc000028480, {0xa2b078, 0xc000028440}, 0xc0000b0000, 0xc0000aa000)
      	C:/Users/mattn/go/pkg/mod/github.com/a-h/[email protected]/cmd/templ/lspcmd/handler.go:230 +0x24b
      github.com/a-h/templ/cmd/templ/lspcmd.(*Proxy).Handle(0xc000028480, {0xa2b078, 0xc000028440}, 0x0, 0xc0000aa000)
      	C:/Users/mattn/go/pkg/mod/github.com/a-h/[email protected]/cmd/templ/lspcmd/handler.go:196 +0x75d
      github.com/sourcegraph/jsonrpc2.(*Conn).readMessages(0xc00014a120, {0xa2b078, 0xc000028440})
      	C:/Users/mattn/go/pkg/mod/github.com/sourcegraph/[email protected]/jsonrpc2.go:553 +0x2f8
      created by github.com/sourcegraph/jsonrpc2.NewConn
      	C:/Users/mattn/go/pkg/mod/github.com/sourcegraph/[email protected]/jsonrpc2.go:334 +0x28c
      
      
      opened by mattn 3
    • fix: don't self-close empty script tags

      fix: don't self-close empty script tags

      Hi, I encountered a bug in v2 of templ where <script src="..."></script> elements are being self-closed by the formatter and end up looking like this:

      <script src="..."/></script>
      

      I added a reproducing test case and fixed it, but I'm not sure if my implementation change makes sense.

      Thanks for the review and thanks for making templ, I love the concept!

      opened by brentd 2
    • Write files to buffer rather than directly to files

      Write files to buffer rather than directly to files

      After some profiling of the generate command I had some interesting results.

      I created a 10MB templ file which is the largest allowed then profiled the generation of it.

      Ignoring any small effects profiling had on the speed it took around 2 minutes 50 seconds consistently. Peaking at around 20MB allocated memory.

      According to pprof the majority of this was time spent in syscall. Using a file as an io.Reader was calling syscall a lot, which is slow.

      So I tried writing to a buffer then writing the buffer and reduced this drastically.

      It took about 8 seconds with a similar memory peak.

      opened by joerdav 2
    • Implicit package selection

      Implicit package selection

      As noted in #35, templ currently requires a `{% package "packageName" %} at the top of each file.

      This is the same behaviour as Go, but to make it faster to write templates, the following approach could be used:

      • [x] Ensure that the parser supports missing package instructions, and inserts them into the object model.
      • [x] Ensure that templ fmt inserts missing package instructions.

      The automatic package algorithm:

      • Uses the contents of the package instruction in the *.templ file if there is one.
      • Uses the name of the directory as the package name if it is a valid Go identifier (see https://go.dev/ref/spec#identifier)
      • Uses main as the package if it's not, or a directory context is not available (e.g. parsing a string).

      This is simpler than attempting to read any Go files that already exist in the directory.

      opened by a-h 2
    • Implement http.Handler so that templates can be rendered directly

      Implement http.Handler so that templates can be rendered directly

      At the moment, to output a template over HTTP, the template has to be wrapped by some boilerplate code.

      func main() {
      	http.Handle("/posts", PostHandler{})
      	http.ListenAndServe(":8000", nil)
      }
      
      type PostHandler struct{}
      
      func (ph PostHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
      	posts := []Post{
      		{
      			Name:   "templ",
      			Author: "author",
      		},
      	}
      	err := postsTemplate(posts).Render(r.Context(), w)
      	if err != nil {
      		log.Println("error", err)
      	}
      }
      

      If templ components implemented http.Handler, it could be shortened to:

      func main() {
      	http.Handle("/posts", postsTemplate([]Post{{ Name: "templ", Author: "author"}}))
      	http.ListenAndServe(":8000", nil)
      }
      

      The component's ServeHTTP method would render a minimal error page, and return a 500 status code if the template failed to render.

      In the case that the author of the template wanted to return a non HTTP 200 status code etc. then they'd use HTTP middleware to do it.

      func main() {
      	http.Handle("/404", withStatus(http.NotFound, errorTemplate404()))
      	http.ListenAndServe(":8000", nil)
      }
      
      func withStatus(code int, next http.Handler) http.Handler {
      	return http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
      		w.WriteHeader(code)
      		next.ServeHTTP(w, r)
      	})
      }
      
      opened by a-h 2
    • Cannot parse empty attribute

      Cannot parse empty attribute

      nav.templ:

          1 {% package templates %}
          2 
          3 {% import "container/types" %}
          4 
          5 {% templ NavTemplate(apps []types.AppConfig) %}
          6     <nav data-turbo-permanent>       
          7         <h1>{%= "Go Feed Me" %}</h1>
          8         <ul>
          9             {% for _, a := range apps %}
         10                 <li>
         11                     <a href={%= a.Url %}  data-turbo-frame="container">{%= a.Name %}</a>
         12                 </li>
         13             {% endfor %}
         14         </ul>
         15     </nav>
         16 {% endtempl %}
      

      nav_templ.go (it stops after the imports):

      >>  1 // Code generated by templ DO NOT EDIT.
          2 
          3 package templates
          4 
      >>  5 import "github.com/a-h/templ"
      >>  6 import "context"
      >>  7 import "io"
      >>  8 import "container/types"
      

      Strangely enough this also fails when I try 'data-turbo-permanent="true"' potentially todo with the dashes aswell?

      Tried doing a "valueless" attribute without dashes like "hidden". That also resulted in the same outut.

      opened by joerdav 2
    • proposal: storybook - add select box option for variants

      proposal: storybook - add select box option for variants

      I'm working on a component library for tailwindcss, but I think there's some missing functionaility in the storybook package.

      I plan on adding these once templ is upgraded to 1.18 as I think the solution will be different with generics.

      Most component libraries have a concept of variants, so having a dropdown for these would be extremely valuable.

      opened by joerdav 0
    • Proposal: create a templ init comand to create a starter website

      Proposal: create a templ init comand to create a starter website

      The starter website might have:

      • /scripts - TypeScript or ES6 in here would be transpiled by esbuild.
        • package.json - To enable the use of 3rd party JavaScript packages.
        • default.ts - Will end up in the scripts/dist directory as default.js.
      • /routes
        • /default
          • default.templ - Template for the / page.
          • default.go - HTTP handler for /.
        • /about
          • about.templ - Template for the /about page.
          • about.go - HTTP handler for /about.
        • /register
          • register.templ - Template for the /register page.
          • register.go - HTTP handler for /register.
      • router.go - provides a router.
      • /layout
        • /global
          • page.templ - Overall template
          • header.templ - Header template
          • footer.templ - Footer template
      • /static
        • logo.png - image to include.
      • main.go - Entry point that runs everything as a web server.
      • README.md - includes tasks that demonstrate how to run everything, including templ generate, esbuild operations, hot reload.

      Ideally, it would also have a way to output a CDK compatible version for AWS hosting, and maybe include a Github Actions or similar way of building.

      Maybe there's a template project builder that can be applied for this purpose?

      opened by a-h 0
    • Proposal: templ website and playground

      Proposal: templ website and playground

      It might be useful to have a templ playground so that people can share snippets of code and try out using templ without having to install it first.

      It would need to have the following features:

      • A place to write Go code for the input struct definition, input data, and any helper functions, e.g. date formatting.
      • A place to write a templ file, which will take the input, e.g.:
      {% templ Display(in Input) %}
      {% endtempl %}
      
      • Output areas to display the generated HTML code, and the HTML as displayed in the browser.
      • A "Run" button to click to format the code and execute it.
        • Since it's basically going to execute arbitrary code, it would need to run in a Lambda or similar to prevent abuse.
        • Running in a Lambda would require the templ executable to be installed in the Lambda.
      • A "Share" button that creates a shareable web link.
        • This supposes the idea of a backing database, e.g. DynamoDB.
      opened by a-h 0
    • Feedback, Inputs & Proposals

      Feedback, Inputs & Proposals

      I really like templ. I hope it have a great future. I have made some notes and thoughts while using it which I want to share now - I write it compact, but let's discuss about some points if you want.

      1. Pointer Receiver Templates

      I commonly use structs as ViewModels. Instead wire up all of those with it's render function manually this could help a lot:

      {% templ (x ViewModel) MyTempl() %}
      

      I used this pattern very much in quicktemplate to make all kind of structures "renderable". Currently in templ I have to create a struct method manually and invoke the render function, for every piece of template.

      2. Autocompletion

      {%! $ %} // cursor
      

      In this scenario a Component is expected. All functions in the current package, which return a Component are possible candidates.

      3. Implicit Package selection

      This line of code is most time redundant because the package name can be derrived by the directory name. When using templ on daily basis I forget this line sometimes. Also, it is not adjusted by the IDE if the name of the package name changes (i.e. the directory name).

      {% package my_pkg %}
      

      4. Comments

      Comment out quickly some code like {% comment %} in quicktemplate. What about HTML comment? Or {{/* a comment */}}

      5. Alternative START/END-Tag

      This point is mabye a matter of taste. On swiss german keyboard, and maybe a lot others, this {% and %} is arduous to write. And you do it a lot when writing templates. Maybe the {{ and }} which are used in regular go templates is not bad at all.

      6. Required Space after START-Tag and before END-Tag

      The space after the {% or before the %} took some getting used to. Strict on the one hand, prone to error on the other. Perhaps the parser could point out this error more clearly - or even autofix it (like go fmt for templ).

      7. The Context

      A really happy that a context I passed arround the Component Render methods. This way I can access global data or a localizer helper. But this is undocumented feature and somehow not so nice.

      {% if getUser(ctx) != nil %}
         Username: {% getUser(ctx).Username %}
         Age: {% getLocalizer(ctx).FormatNumber(getUser(ctx).Height) %}
      {% endif %}
      

      I have no concrete implementation-idea currently. I make some thoughts... But in the abstract a shortcut to access a global "struct" variable would do the job.

      {% if .user != nil %}
         Username: {% .user.Username %}
         Age: {% .i18n.FormatNumber(.user.Height) %}
      {% endif %}
      

      An easier implementation would be: A pointer to a struct could be passed via context value, while templ make it visible in a variable x.

      {% if x.user != nil %}
         Username: {% x.user.Username %}
         Age: {% x.i18n.FormatNumber(x.user.Height) %}
      {% endif %}
      

      In both cases the qualified-id must be specified somewhere (for generation and LSP)

      Don't get me wrong, I'm not a make it all global type of guy. But it reduces all this argument passtroughts of really important things (i18n, basic user info etc.). It's just a third source of (typed) data beside receiver variable + argument variables.

      8. The Writer

      If I'm not mistaken, virtual calls are 5 times slower than direct method calls. And a template calls it thousands of times including the error check. A benchmark might help. Maybe it would be interesting to have a look at the implementation of Quicktemplate, where among other things an acquirable byte buffer comes into play. Another idea would be to be able to define a writer type via CLI argument. If I manually implement templ components in go, the io.Writer interface is a bit tedious to work with directly.

      9. Switch case improvement

      Instead:

      {% switch p.Type %}
      	{% case "test" %}
      		<span>{%= "Test user" %}</span>
      	{% endcase %}
      	{% case "admin" %}
      		<span>{%= "Admin user" %}</span>
      	{% endcase %}
      	{% default %}
      		<span>{%= "Unknown user" %}</span>
      	{% enddefault %}
      {% endswitch %}
      

      What about:

      {% switch p.Type %}
      	{% case "test" %}
      		<span>{%= "Test user" %}</span>
      	{% case "admin" %}
      		<span>{%= "Admin user" %}</span>
      	{% default %}
      		<span>{%= "Unknown user" %}</span>
      {% endswitch %}
      

      10. Range improvement

      Instead:

      {% if len(p.Addresses) == 0 %}
         <span>No addresses</span>
      {% endif %}
      {% for _, v := range p.Addresses %}
      	<li>{%= v.City %}</li>
      {% endfor %}
      

      What about:

      {% for _, v := range p.Addresses %}
      	<li>{%= v.City %}</li>
      {% default %}
         <span>No addresses</span>
      {% endfor %}
      

      This construct is rarely found in template languages. To be honest, I have no idea why.

      11. Else-If support

      Mabye the switch is a better alternative anyway when it have at least the same capacibilities.

      12. Compile a specific file only

      Currently recursively all templ files are compiled. I configured my IDE or modd to autocompile templ files on every change.

      opened by aight8 11
    • LSP: Enable automatic insertion of HTML close tags

      LSP: Enable automatic insertion of HTML close tags

      As per https://github.com/microsoft/language-server-protocol/issues/715

      It looks like it's possible to intercept edits, and then use the https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit feature initiated from the server to insert the end tag, just like the XML example in the issue.

      I'm currently working on doing this for the templ language elements, so there will be something to base this on.

      enhancement good first issue 
      opened by a-h 1
    Releases(v0.2.171)
    Owner
    Adrian Hesketh
    Adrian Hesketh
    Amber is an elegant templating engine for Go Programming Language, inspired from HAML and Jade

    amber Notice While Amber is perfectly fine and stable to use, I've been working on a direct Pug.js port for Go. It is somewhat hacky at the moment but

    Ekin Koc 894 Jul 1, 2022
    Simple system for writing HTML/XML as Go code. Better-performing replacement for html/template and text/template

    Simple system for writing HTML as Go code. Use normal Go conditionals, loops and functions. Benefit from typing and code analysis. Better performance than templating. Tiny and dependency-free.

    Nelo Mitranim 4 Apr 13, 2022
    pongo2 is a Django-syntax like templating-language

    Django-syntax like template-engine for Go

    Florian Schlachter 2.3k Jun 25, 2022
    An ERB-style templating language for Go.

    Ego Ego is an ERb style templating language for Go. It works by transpiling templates into pure Go and including them at compile time. These templates

    Ben Johnson 525 Jun 26, 2022
    A sweet velvety templating package

    Velvet Velvet is a templating package for Go. It bears a striking resemblance to "handlebars" based templates, there are a few small changes/tweaks, t

    Buffalo - The Go Web Eco-System 73 Apr 14, 2022
    Toothpaste is a simple templating engine for Go web applications, inspired by Blade/Twig

    A simple HTML templating engine in Go inspired by Twig. Currently supports Variables, Escaping, If statements, Templating and Functional variables.

    Mats 2 Dec 29, 2021
    Package damsel provides html outlining via css-selectors and common template functionality.

    Damsel Markup language featuring html outlining via css-selectors, extensible via pkg html/template and others. Library This package expects to exist

    Daniel Skinner 25 Jan 24, 2022
    Goview is a lightweight, minimalist and idiomatic template library based on golang html/template for building Go web application.

    goview Goview is a lightweight, minimalist and idiomatic template library based on golang html/template for building Go web application. Contents Inst

    foolin 284 Jun 21, 2022
    A template to build dynamic web apps quickly using Go, html/template and javascript

    gomodest-template A modest template to build dynamic web apps in Go, HTML and sprinkles and spots of javascript. Why ? Build dynamic websites using th

    Adnaan Badr 80 Jun 27, 2022
    Golang Echo and html template.

    golang-website-example Golang Echo and html template. move GitHub repository for hello to golang-website-example Visual Studio Code Run and Debug: lau

    Ocki Bagus Pratama 0 Feb 4, 2022
    HTML template engine for Go

    Ace - HTML template engine for Go Overview Ace is an HTML template engine for Go. This is inspired by Slim and Jade. This is a refinement of Gold. Exa

    Keiji Yoshida 817 May 31, 2022
    Wrapper package for Go's template/html to allow for easy file-based template inheritance.

    Extemplate Extemplate is a small wrapper package around html/template to allow for easy file-based template inheritance. File: templates/parent.tmpl <

    Danny van Kooten 49 Jun 14, 2022
    Fast, powerful, yet easy to use template engine for Go. Optimized for speed, zero memory allocations in hot paths. Up to 20x faster than html/template

    quicktemplate A fast, powerful, yet easy to use template engine for Go. Inspired by the Mako templates philosophy. Features Extremely fast. Templates

    Aliaksandr Valialkin 2.5k Jun 22, 2022
    Safe HTML for Go

    Safe HTML for Go safehtml provides immutable string-like types that wrap web types such as HTML, JavaScript and CSS. These wrappers are safe by constr

    Google 278 May 19, 2022
    mold your templated to HTML/ TEXT/ PDF easily.

    mold mold your templated to HTML/ TEXT/ PDF easily. install go get github.com/mayur-tolexo/mold Example 1 //Todo model type Todo struct { Title stri

    Mayur Das 0 Jun 7, 2019
    Code your next Go web project with (a) Mojito! No matter if its an API or a website, go-mojito assists you with dependency injection, simple routing, custom request / response objects and template rendering

    Go-Mojito is a super-modular library to bootstrap your next Go web project. It can be used for strict API-only purposes as well as server-side renderi

    Infinytum 17 May 1, 2022
    The mustache template language in Go

    Overview mustache.go is an implementation of the mustache template language in Go. It is better suited for website templates than Go's native pkg/temp

    Michael Hoisie 1k Jun 30, 2022
    Tool for creating advent of code template

    AOC Template Generator ?? I'm tired of creating a folder for each day with the same file structure so why not having a tool to automate that proccess

    Francisco Carmona Suárez 1 Dec 9, 2021
    Article spinning and spintax/spinning syntax engine written in Go, useful for A/B, testing pieces of text/articles and creating more natural conversations

    GoSpin Article spinning and spintax/spinning syntax engine written in Go, useful for A/B, testing pieces of text/articles and creating more natural co

    Miles Croxford 37 Jun 13, 2022