Nimbus Templates (.nimbus)
Nimbus includes its own lightweight, production-grade template engine featuring an Edge/Blade-inspired syntax. Template files use the .nimbus extension and reside in the resources/views/ directory. Compiled down to Go's native html/template, they deliver absolute safety against XSS attacks combined with a developer-friendly syntax.
§ Overview
Edge/Blade syntax allows you to compose clean HTML markup on the server and stream it to the client. Since templates execute on the server-side, they integrate natively with authentication guards, authorization checks, sessions, and request helper contexts.
Templates are automatically compiled at startup, and in development mode, they are refreshed on file change without requiring a server reboot.
§ Your First Template
Let's create a simple page to display a list of blog posts. This example demonstrates the fundamental workflow of rendering templates in Nimbus.
1. Create the Template File
Templates are stored in the resources/views/ directory. Create a new template file at resources/views/pages/posts/index.nimbus:
@layout('layout-app')
<div class="max-w-xl mx-auto py-8">
<h1 class="text-2xl font-bold mb-6">Blog Posts</h1>
@each(post in posts)
<div class="mb-6 border-b pb-4">
<h2 class="text-xl font-semibold text-slate-900 dark:text-white">
{{ post.Title }}
</h2>
<p class="text-slate-600 mt-2">
{{{ excerpt(post.Content, 280) }}}
</p>
</div>
@end
</div>
Understanding the Syntax:
@layout('layout-app')inherits the global HTML shell (boilerplate, head, navigation).@each(post in posts) ... @endloops over a Go slice or array.{{ post.Title }}outputs HTML-escaped text.{{{ excerpt(post.Content, 280) }}}outputs unescaped HTML content (use only with trusted content).
2. Register the Route and Controller
Register a route and serve it from your controller handler in Go:
package start
import (
"github.com/CodeSyncr/nimbus/router"
"app/controllers"
)
func RegisterRoutes(r *router.Router) {
r.Get("/posts", controllers.PostsIndex)
}
Render the template from the controller by passing the view path and state:
package controllers
import (
"github.com/CodeSyncr/nimbus/http"
"app/models"
)
func PostsIndex(c *http.Context) error {
posts, err := models.GetRecentPosts()
if err != nil {
return err
}
// Render resources/views/pages/posts/index.nimbus
return c.View("pages/posts/index", map[string]any{
"posts": posts,
})
}
§ Understanding Template State
The data map you pass to c.View(name, state) is called the template state. All keys in this map become available as variables inside the template context.
In addition to explicit data variables, Nimbus shares framework globals with every template:
request: Access URL query parameters, headers, or client IP.auth: Check authentication state and active user claims.session: Retrieve flash data, messages, or session store variables.- Built-in helper functions (e.g.
route(),csrfField(),excerpt()).
§ Template Syntax Refresher
Outputting Variables
<p>{{ user.Name }}</p>
Outputting Unescaped HTML
<div>{{{ post.BodyHtml }}}</div>
Conditionals
@if(user.IsAdmin)
<span class="badge">Admin</span>
@elseif(user.IsModerator)
<span class="badge">Moderator</span>
@else
<span class="badge">User</span>
@end
Loops
@each(item in items)
<li>{{ item }}</li>
@end
Evaluating Logic & Expressions
{{ len(items) > 0 ? "Items available" : "No items" }}