Jet template engine

Overview

Jet Template Engine for Go

Build Status Build status Join the chat at https://gitter.im/CloudyKit/jet

Jet is a template engine developed to be easy to use, powerful, dynamic, yet secure and very fast.

  • simple and familiar syntax
  • supports template inheritance (extends) and composition (block/yield, import, include)
  • descriptive error messages with filename and line number
  • auto-escaping
  • simple C-like expressions
  • very fast execution – Jet can execute templates faster than some pre-compiled template engines
  • very light in terms of allocations and memory footprint

v6

Version 6 brings major improvements to the Go API. Make sure to read through the breaking changes before making the jump.

Docs

Example application

An example to-do application is available in examples/todos. Clone the repository, then (in the repository root) do:

  $ cd examples/todos; go run main.go

IntelliJ Plugin

If you use IntelliJ there is a plugin available at https://github.com/jhsx/GoJetPlugin. There is also a very good Go plugin for IntelliJ – see https://github.com/go-lang-plugin-org/go-lang-idea-plugin. GoJetPlugin + Go-lang-idea-plugin = happiness!

Contributing

All contributions are welcome – if you find a bug please report it.

Contributors

  • José Santos (@jhsx)
  • Daniel Lohse (@annismckenzie)
  • Alexander Willing (@sauerbraten)
Issues
  • Add ability to use an http.FileSystem for looking up templates

    Add ability to use an http.FileSystem for looking up templates

    Together with a tool like vfsgen this can be used to embed the templates into the compiled go binary. This makes deployments simpler and faster but should be used in conjunction with the development mode and a stubbed (read: nil) http.FileSystem.

    opened by annismckenzie 27
  • Pass new scope to yield and include

    Pass new scope to yield and include

    I'm trying to imitate some of the approaches other frameworks take to provide a common library of reusable parts for forms. The {{import "form_theme_bootstrap.jet" }} and the {{yield}} directive are good building blocks to achieve that. The problem arrises when trying to pass local variables to use when yielding, for example, an input-field block in the sense that it's not possible (at least I don't see a way).

    Let me explain with some code:

    <!-- file: "form_theme_bootstrap.jet" -->
    {{block input-field}}
      <div class="{{container_class}} {{if has_error}}invalid{{else}}valid{{end}}">
        <input id="{{name}}" name="{{name}}" value="{{value}}" />
        {{if has_error}}
          <div class="form_error">{{error}}</div>
        {{end}}
      </div>
    {{end}}
    
    <!-- file: "user_form_edit.jet" -->
    {{import "form_theme_bootstrap.jet"}}
    
    <form>
      <!-- not sure how passing a hash/map of local variables would work  -->
      {{yield input-field { name: "firstname", value: .Firstname, has_error: errs.HasErrorFor("Firstname"), error: errs.ErrorFor("Firstname"), container_class: "" }  }}
    </form>
    

    Do you see what I'm trying to do? Having a way to construct a map on the fly in the template would be really great, that way I could use that as the context for the yield.

    Thanks again for all the hard work and this great library!

    enhancement feature 
    opened by annismckenzie 21
  • include & extend don't handle absolute paths correctly

    include & extend don't handle absolute paths correctly

    Assuming I have a template at foo/baz.jet, {{ include "foo/bar.jet" }} will make resolveNameSibling try to resolve foo/foo/bar.jet before resolving foo/bar.jet: https://github.com/CloudyKit/jet/blob/33cfc27b3e00072655fdb3af24c3253ea5bffb8f/template.go#L153

    name will be "foo/bar.jet", sibling will be "foo/baz.jet".

    I would like to simplify template resolving & loading and could fix this issue at the same time. Seeing as we are making breaking changes with v3 anyway, I would like to make jet handle paths like most software does:

    • ./asd.jet, asd.jet, ../asd.jet will be treated as relative paths and will be resolved relative to the current template's path
    • /foo.jet, /foo/bar.jet will be treated as absolute paths and will be looked up directly in the templates cache and/or the loader, without using the current template's path

    Do you want me to proceed with this? Did I miss something?

    opened by sauerbraten 16
  • Regression: Ranging over slice from Fast Function

    Regression: Ranging over slice from Fast Function

    I've observed a regression caused by https://github.com/CloudyKit/jet/commit/6d666f94dfe73004a23e8e41d7532ac8602c9132 (#112)

    Summary

    For this template:

    {{sorted := SortApples(myApples)}}
    {{range i, apple := sorted}}
    	{{i}}: {{apple.GetFlavor()}}
    {{end}}
    

    (Where SortApples is a fast function aka jet.Func.)

    Jet was able to range over the returned slice up until https://github.com/CloudyKit/jet/commit/6d666f94dfe73004a23e8e41d7532ac8602c9132 at which point the template execution would error with: there is no field or method "GetFlavor" in main.Apple

    Example Project

    https://github.com/tooolbox/jet-example has two branches:

    • v2.1.2 (working)
    • v2.1.3-6d666f94dfe7 (broken)

    I understand we're now going for a v3 with these commits, so breaking changes might be expected, but this seems like it would hurt fast functions a lot.

    cc @sauerbraten

    opened by tooolbox 15
  • Variable in block parameter

    Variable in block parameter

    I have this

    Go code:

    ...
    func theaterPage(client *gin.Context) {
    	channel := client.Param("channel") // is just a string like "Left4Bot"
    	view, err := views.GetTemplate("theater.tpl.html")
    	if err != nil {
    		return
    	}
    	view.Execute(client.Writer, jet.VarMap{
    		"ChannelName": reflect.ValueOf(channel),
    	}, nil)
    }
    

    My block:

    {{block embedPlayer(channel="toby3d", autoplay=false)}}
    	<iframe src="//www.hitbox.tv/#!/embed/{{ channel }}?autoplay={{ autoplay }}" width="360" height="640" frameborder="0" allowfullscreen></iframe>
    {{end}}
    

    My template:

    {{ block body() }}
    	<main>
    		{{ yield embedPlayer(channel=.ChannelName, autoplay=true) }}
    	</main>
    {{ end }}
    

    And this does not work. How can I implement a variable in a block parameter?

    opened by toby3d 14
  • Fix inconsistency with add operator, Added switch/case, Added default block values, Filter block with format function

    Fix inconsistency with add operator, Added switch/case, Added default block values, Filter block with format function

    Add: "2" + 1 - output: 3 1 + "2" - output: 3 "2" + "2" - output: 4 "2" + "2z" - output: 22z

    Switch/Case with empty case as default : {{ switch power }} {{ case 10 }} Value is 10 {{ end }} {{ case }} This is default case {{ end }} {{ end }}

    Default block:

    • Example 1: {{ Size = 10 }} {{ default }} {{ Size = 20 }} {{ end }} Size is {{ Size }} cm output : Size is 10 cm

    • Example 2: {{ default }} {{ Size = 20 }} {{ end }} Size is {{ Size }} cm output : Size is 20 cm

    Filter block with format function: {{ filter format("<-- %v -->") }} This is formatted text {{ end }} output: <-- This is --> <-- formatted text -->

    opened by maxime1907 13
  • how to call template function?

    how to call template function?

    I use a template variable to send a function, how to call it in the template?

    func Full() string {
    	return "ok"
    }
    
    data := make(map[string]interface{})
    data["Full"]= Full
    
    opened by dxvgef 13
  • Allow accessing struct fields using lower case names

    Allow accessing struct fields using lower case names

    @annismckenzie Curious your thoughts about this - internally my structs have a lot of *string, *int, *bool, etc. where the values can be nil if not set explicitly. In Jet if a structure has a field and its value is nil then it appears to be the same as it not existing -- https://github.com/CloudyKit/jet/blob/master/eval.go#L1111

    From a templating perspective I'd think they would be treated as safe values such as "", 0, false, etc. if it exists unless I'm explicitly comparing them with nil with isset. If I made a change to differentiate between a field that doesn't exist and a field that does exist but it set to nil, is that a change you would be interested in?

    I was also thinking that using field tags to have a preferred name for struct fields (like encoding/json does) so that its not so obvious that Golang is involved in rendering the template or to obfuscate where the value might be coming from. It looks like there is just couple places in eval.go where a change would be needed to implement that. Would you be interested in that change if I got it working?

    opened by zfLQ2qx2 11
  • Clean up template loading

    Clean up template loading

    This fixes #127 (paths in templates will be handled as described there) and #128.

    Breaking changes:

    • OSFileSystemLoader only supports a single directory instead of multiple
    • the AddPath and AddGopathPath functions were removed
    • ~~Loader.Exists no longer returns the "real" file name~~
    • after include or extends, relative paths will no longer be looked up as absolute paths (see #127)

    I removed the ability to have multiple directories managed by a single OSFileSystemLoader becaue of #128, and because I feel like

    1. most users will have all templates in a single directory, organized into subdirectories there,
    2. with the new handling of relative paths, the top directory name will appear in paths less often, and
    3. you can always use loaders.Multi to get back the functionality a single OSFileSystemLoader provided before

    I added a note to loaders.Multi warning that lookup order depends on the order in which loaders are passed in during instantiation.

    opened by sauerbraten 11
  • Revamp Documentation

    Revamp Documentation

    While the Jet Wiki here on GitHub has some useful information and examples, it is not easy to use as a reference document. I propose this gets cleaned up!

    1. In a Godoc, all objects and methods are laid out in a row with the exact signatures, with an index to find what you need. With Jet's wiki, the important syntax examples or function names are buried in paragraphs of tutorial-style explanation.
    2. The titles of each page are not clearly representative of their content. For example, "Advanced" specifically contains interfaces you can implement to increase speed or customize rendering output. When answering the question "What's that piece of syntax?" the answer may be in the "Expressions" page or maybe the "Jet template syntax" page, or maybe some other page entirely.
    3. Some commands do not have precisely defined behavior, but are only explained through examples. The best example of this is yield.
    4. The documentation is sprinkled with "new in v2". Now that v2 has been out for some time and is likely the standard, this commentary could be removed.

    I would love to be able to open a PR on the Wiki, but GitHub doesn't seem to support that 😞

    opened by tooolbox 11
  • dump() built-in function (#111)

    dump() built-in function (#111)

    The following is - perhaps naive? - attempt to satisfy requirement described in issue #187. Let me know what you think - Jet internals are mystery to me, at least for now.

    To be honest, I am not sure why comparison against master contains also changes implemented by 4beb91506060bd8900ec0f7d42e401d21300350f and f67928da339229e4ccb9b198a6428b8f077cf1c9 - that is confusing.

    Regards,

    Jan

    opened by jan-herout 10
  • go 1.17.7 new issue

    go 1.17.7 new issue

    Hi there, please consider following:

    type Mode uint8
    
    const (
    	Reserved Mode = iota
            Foo
            Bar
    )
    
    func (m Mode) String() string {
    	switch m {
    	case Foo:
    		return "Foo"
    	case Bar:
    		return "Bar"
           default:
    		return "Reserved"
    	}
    }
    

    For golang < 1.17.7 I was using {{ .Mode }} and it was displayed as 0, 1, 2 Now it is displayed as Reserved, Foo, Bar.

    So Stringer interface somehow kicks in.

    There is easy workaround, but I just want to know if there is need to adjust Jet or file an issue to golang directly.

    Thanks and all the best

    opened by jdvjdv82 0
  • Put back YieldTemplate

    Put back YieldTemplate

    I put back YieldTemplate which was removed earlier. I am using it in my code to "insert" template files. Please put it back. Thank you very much in advance.

    opened by SVyatoslavG 0
  • Unable to include - template could not be found

    Unable to include - template could not be found

    I'm sure this is a stupidly simple problem - but I'm staring at it and just can't see what is going on!

    Views are in a folder two levels below the executable (in www/views) and jet is configured like this:

    engine = jet.New("www/views", ".jet")

    I have a template www/views/register.jet that works just fine, with the page displayed. I then add another template to the folder 'header.jet' and add the following to the top of register.jet

    {{ include "./header.jet" }}

    But if I reload the page, I get an error:

    Jet Runtime Error ("/register":1): template /header.jet could not be found

    I have tried various things such as addint the path, such as www/views/header.jet but cannot get it to work.

    Thanks for any thoughts..

    opened by richp10 2
  • Adding support for Go 1.16's FS interface

    Adding support for Go 1.16's FS interface

    Go 1.16 introduces the fs package and a FS interface. Implementing a Jet loader for this seems like an ideal use case. Are there any plans for this, would it be easy?

    EDIT: actually, maybe it is as simple as this?

    type StdFileSystemLoader struct{ fs.FS }
    
    func (l StdFileSystemLoader) Open(templatePath string) (io.ReadCloser, error) {
    	return l.FS.Open(templatePath)
    }
    
    func (l StdFileSystemLoader) Exists(templatePath string) bool {
    	_, err := l.Open(templatePath)
    	return err == nil && !os.IsNotExist(err)
    }
    
    opened by advanderveer 5
  • Question about IDE

    Question about IDE

    Hello:

    I really like this package. The only thing I have not figured out (and it's not a criticism, but a question) is how to use it in an IDE without having the template show up as having errors. For example, if I associate the file type *.jet with HTML, then if statements show up as being errors. If I associate *.jet with Golang templates, I get a different set of errors. I use Jetbrains Goland.

    I see there is an old plugin, but it doesn't work with the latest releases of Jetbrains software.

    I don't suppose anyone has a suggestion for a set up that would make the errors go away?

    Thanks again for this great package.

    opened by tsawler 1
  • using custom delimiters breaks recognition of comments

    using custom delimiters breaks recognition of comments

    After tinkering with Jet using custom delimiters I was able to reproduce the following problem directly on Jet test suite.

    • modified file : testData/custom_delimiters.jet
    • first line now reads: {* comment *}[[ . ]]
    • test fails
    c:\GitHub\jet>go test
    --- FAIL: TestParseTemplateWithCustomDelimiters (0.00s)
        parse_test.go:46: Unexpected tree on custom_delimiters.jet Got:
            {* comment *}{{.}}
            {{singleValue}}
            {{nil}}
            {{""}}
            {{val.Field}}
            {{url("")}}
            {{url("", "") | pipe}}
            {{url("") | pipe | pipe}}
            {{url("", "").Field | pipe}}
            {{url("", "").Method("") | pipe}}
            Expected:
            {{.}}
            {{singleValue}}
            {{nil}}
            {{""}}
            {{val.Field}}
            {{url("")}}
            {{url("", "") | pipe}}
            {{url("") | pipe | pipe}}
            {{url("", "").Field | pipe}}
            {{url("", "").Method("") | pipe}}
    FAIL
    exit status 1
    FAIL    github.com/CloudyKit/jet/v6     1.775s
    

    Might it be that this is the source of the problem? See below... It seems that methow WithDelims should accept full delimiter set to allow for correct parsing of comments.

    // state functions
    func lexText(l *lexer) stateFn {
    	for {
    		// look for first character of the custom delimiter. if not found, break
    		// this EXCLUDES comment defined as {* from the parse, and sends it on output as text ???
    		if i := strings.IndexByte(l.input[l.pos:], l.leftDelim[0]); i == -1 {
    			l.pos = Pos(len(l.input))
    			break
    		} else {
    			l.pos += Pos(i)
    			if strings.HasPrefix(l.input[l.pos:], l.leftDelim) {
    				ld := Pos(len(l.leftDelim))
    				trimLength := Pos(0)
    				if strings.HasPrefix(l.input[l.pos+ld:], leftTrimMarker) {
    					trimLength = rightTrimLength(l.input[l.start:l.pos])
    				}
    				l.pos -= trimLength
    				if l.pos > l.start {
    					l.emit(itemText)
    				}
    				l.pos += trimLength
    				l.ignore()
    				return lexLeftDelim
    			}
    			// with custom delimiters, this will NEVER be activated
    			if strings.HasPrefix(l.input[l.pos:], leftComment) {
    				if l.pos > l.start {
    					l.emit(itemText)
    				}
    				return lexComment
    			}
    		}
    		if l.next() == eof {
    			break
    		}
    	}
    	// Correctly reached EOF.
    	if l.pos > l.start {
    		l.emit(itemText)
    	}
    	l.emit(itemEOF)
    	return nil
    }
    
    
    opened by jan-herout 2
Releases(v6.1.0)
  • v6.1.0(Mar 5, 2021)

    Added dump() built-in function for development/debugging: https://github.com/CloudyKit/jet/blob/master/docs/builtins.md#dump Thanks @jan-herout for contributing this! (#189)

    Source code(tar.gz)
    Source code(zip)
  • v6.0.2(Jan 4, 2021)

  • v6.0.1(Nov 6, 2020)

  • v6.0.0(Nov 5, 2020)

  • v5.1.0(Oct 13, 2020)

  • v5.0.3(Sep 22, 2020)

    Bugfix release: Concurrent use of a Set of templates could cause data races via a shared array for the argument values of function calls using the reflect package. https://github.com/CloudyKit/jet/commit/0e59615c7a6c1e1046ef26e020eedb01f7fa651f is the only commit since v5.0.2.

    Source code(tar.gz)
    Source code(zip)
  • v3.0.1(Sep 8, 2020)

  • v5.0.2(Aug 15, 2020)

    Bugfix release: When calling variadic functions, the list of argument expressions was not correctly mapped to the variadic argument slice needed to call the function using reflection. See #171 for more information. Thanks, @alresvor!

    Source code(tar.gz)
    Source code(zip)
  • v5.0.1(Aug 12, 2020)

    Bugfix release: When using discard syntax in a simple "let" assignment, i.e. one where the right side does not return an optional second value like a map lookup for example, the right side wasn't evaluated. For example, these all worked as expected and the right side was evaluated:

    _ := myMap["foo"]
    _ = myMap["foo"]
    
    _, ok := myMap["foo"]
    _, ok = myMap["foo"]
    
    _ = foo()
    

    But this did not actually execute foo():

    _ := foo()
    

    f67efbcea6a7fdd47b716daebca8f9947e86422d fixes this case and is the only commit in v5.0.1 that isn't in v5.0.0.

    Source code(tar.gz)
    Source code(zip)
  • v5.0.0(Jul 29, 2020)

    With version 5, Jet gets Go-like discard syntax (_ := stillRuns(), #168) and allows you to specify the argument slot of the piped value when pipelining (2 | repeat("foo", _), #169).

    This means that _ is now a reserved symbol, as noted in v5's breaking changes: https://github.com/CloudyKit/jet/blob/master/docs/changes.md#v5.

    Source code(tar.gz)
    Source code(zip)
  • v4.1.0(Jul 20, 2020)

    Allows concatenating string and []byte.

    This is useful for using the return value of the built-in json() function, for example:

    {{ "{foo:"+json("asd")+"}" }}

    This would previously render as {foo:[34 97 115 100 34]} but will now render as {foo:"asd"}).

    Source code(tar.gz)
    Source code(zip)
  • v4.0.2(Jul 14, 2020)

  • v4.0.1(Jul 14, 2020)

    Allows using parentheses syntax for function calls in pipelines, for example "foo" | repeat(2) | upper is now valid, when only "foo" | repeat: 2 | upper would work previously.

    Source code(tar.gz)
    Source code(zip)
  • v4.0.0(Jul 3, 2020)

    Version 4 brings a lot of bug fixes and improvements as well as updated documentation, but make sure to read through the breaking changes before making the jump.

    Feel free to update and report any bugs (or undocumented breaking changes). I'm quite sure this release won't be perfect since there went a lot of work into it and the tests still leave a lot to be desired (and also because it's my first release).

    Source code(tar.gz)
    Source code(zip)
  • v2.1.2(Nov 4, 2017)

    This release contains:

    • testing via TravisCI in the latest 2 Go versions (#69, #70)
    • aid in debugging when a template is included via the includeIfExists where the template is found but fails to render properly; the error is now bubbled up (#67) – thanks to @diogogmt
    Source code(tar.gz)
    Source code(zip)
  • v2.0.1(Aug 29, 2016)

    • fixed negative numbers in the template throwing an error – this happened, for example, when using {{ replace("My Name Is", " ", "_", -1) }} which would error (the last parameter of Go's replace function denotes the maximum number of replacements to perform, -1 being unlimited) – fixed in fd3b9c029f9492debd228ed9d8e87d89ede0095a
    • added {{ includeIfExists(templateName) }} function that doesn't throw an error if the template to include doesn't exist (useful for dynamic template files to include); it also has a second form where you can use the boolean return value to print something else if the template couldn't be found – documented here, added in 9a0ef3ba8aef44217378909635e555275c37b366
    Source code(tar.gz)
    Source code(zip)
  • v2.0(Aug 14, 2016)

    • much more powerful {{block}} and {{yield}} constructs, allowing local variables, default variables as well as wrapping (read more)
    • fast functions that are up to 50% faster than normal functions; the built-in len and isset functions are implemented as fast functions (read more in the new Advanced section)
    • simple string concatenation in templates obviate the need for sprintf-like functions: {{ "build."+someString+".your-string" }} (see here)
    • isset can now be used to check for the existence of a key in a map (works both with the var map as well as the context, see here and here)
    • templates can now also be imported/included relatively and without the extension; provided the templates' filenames end with .jet, .html.jet or .jet.html (read more)
    Source code(tar.gz)
    Source code(zip)
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 50 Jul 31, 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 292 Aug 2, 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
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 84 Jul 14, 2022
Made from template temporalio/money-transfer-project-template-go

Temporal Go Project Template This is a simple project for demonstrating Temporal with the Go SDK. The full 20 minute guide is here: https://docs.tempo

MarkGorewicz 0 Jan 6, 2022
Go-project-template - Template for a golang project

This is a template repository for golang project Usage Go to github: https://git

KyberNetwork 3 Jul 9, 2022
Go-api-template - A rough template to give you a starting point for your API

Golang API Template This is only a rough template to give you a starting point f

Only Tunes Radio 3 Jan 14, 2022
Api-go-template - A simple Go API template that uses a controller-service based model to build its routes

api-go-template This is a simple Go API template that uses a controller-service

Pedro Espíndula 1 Feb 18, 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 818 Jul 2, 2022
Simple and fast template engine for Go

fasttemplate Simple and fast template engine for Go. Fasttemplate performs only a single task - it substitutes template placeholders with user-defined

Aliaksandr Valialkin 623 Jul 30, 2022
A handy, fast and powerful go template engine.

Hero Hero is a handy, fast and powerful go template engine, which pre-compiles the html templates to go code. It has been used in production environme

Lime 1.5k Jul 30, 2022
A complete Liquid template engine in Go

Liquid Template Parser liquid is a pure Go implementation of Shopify Liquid templates. It was developed for use in the Gojekyll port of the Jekyll sta

Oliver Steele 167 Jul 30, 2022
gtpl is a template engine for glang

gtpl 使用必读 gtpl is a HTML template engine for golang gtpl 是一个 go 语言模板引擎,它能以极快的速度进行模板语法分析。相比 go 语言官方库 html/template,gtpl 的语法有着简练、灵活、易用的特点。

null 5 Jan 23, 2022
The world’s most powerful template engine and Go embeddable interpreter.

The world’s most powerful template engine and Go embeddable interpreter

Open2b 351 Jul 29, 2022
This my project template for making fiber with SSR taste by empowered mustache engine.

SSR-FIBER-TEMPLATE This my project template for making fiber with SSR taste by empowered mustache engine. Folder Hierarchy Name Description configs Co

▲ 2 May 9, 2022
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
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 Jul 18, 2022
Useful template functions for Go templates.

Sprig: Template functions for Go templates The Go language comes with a built-in template language, but not very many template functions. Sprig is a l

null 3.1k Aug 3, 2022
The powerful template system that Go needs

Plush Plush is the templating system that Go both needs and deserves. Powerful, flexible, and extendable, Plush is there to make writing your template

Buffalo - The Go Web Eco-System 723 Jul 29, 2022