Explore the power of server-side rendering with Selmer in Clojure, including templating syntax, template inheritance, dynamic content, and internationalization support.
In the realm of web development, server-side rendering (SSR) plays a crucial role in delivering dynamic, SEO-friendly content to users. For Clojure developers, Selmer stands out as a robust templating engine that facilitates SSR by allowing you to generate HTML on the server side. This section delves into the intricacies of using Selmer for server-side rendering, covering its syntax, template inheritance, dynamic content handling, and internationalization support.
Selmer is a Clojure library inspired by Django’s templating engine, designed to render HTML templates with embedded Clojure expressions. It provides a straightforward way to separate your application’s logic from its presentation, making your codebase more maintainable and scalable.
Selmer’s syntax is both powerful and easy to learn, featuring tags, filters, and expressions that allow you to manipulate data and control the flow of your templates.
At its core, Selmer uses double curly braces ({{ }}
) to denote expressions and {% %}
for control structures. Here’s a simple example:
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<h1>Welcome, {{ user.name }}!</h1>
{% if user.admin %}
<p>You have administrative privileges.</p>
{% else %}
<p>You are a regular user.</p>
{% endif %}
</body>
</html>
In this example, {{ title }}
and {{ user.name }}
are expressions that will be replaced with values from your data model, while the {% if %}
tag is used for conditional logic.
Selmer supports filters, which are functions that transform data before it’s rendered. You can chain filters using the pipe (|
) character:
<p>The price is {{ price | number_format:2 }}</p>
Here, number_format:2
formats the price
variable to two decimal places.
Template inheritance is a powerful feature that allows you to define a base template and extend it in other templates. This promotes reusability and consistency across your application.
A base template typically contains the common structure of your web pages, such as the header, footer, and navigation bar. You can define blocks that child templates can override:
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}My Application{% endblock %}</title>
</head>
<body>
<header>
<h1>My Application</h1>
</header>
<nav>
<!-- Navigation links -->
</nav>
<main>
{% block content %}
<!-- Default content -->
{% endblock %}
</main>
<footer>
<p>© 2024 My Application</p>
</footer>
</body>
</html>
Child templates can extend the base template and provide specific content for each block:
{% extends "base.html" %}
{% block title %}Home Page{% endblock %}
{% block content %}
<h2>Welcome to the Home Page</h2>
<p>This is the main content of the home page.</p>
{% endblock %}
This approach ensures that any changes to the base template automatically propagate to all child templates, reducing duplication and maintenance effort.
One of the key aspects of server-side rendering is the ability to inject dynamic content into your templates. Selmer makes this straightforward by allowing you to pass data from your Clojure handlers to the templates.
In a typical Clojure web application, you define handlers that process requests and return responses. To render a Selmer template, you pass a map of data to the selmer.parser/render
function:
(ns myapp.handler
(:require [selmer.parser :as parser]))
(defn home-page-handler [request]
(let [user {:name "Alice" :admin true}
title "Home Page"]
(parser/render-file "templates/home.html" {:user user :title title})))
In this example, the home-page-handler
function renders the home.html
template, passing the user
and title
data to it. The template can then access these values using the syntax described earlier.
Selmer can handle complex data structures, such as nested maps and sequences. You can iterate over collections using the {% for %}
tag:
<ul>
{% for item in items %}
<li>{{ item.name }}: {{ item.price | number_format:2 }}</li>
{% endfor %}
</ul>
This template snippet iterates over a collection of items
, rendering each item’s name and price.
Supporting multiple languages is crucial for applications targeting a global audience. Selmer provides mechanisms to facilitate internationalization (i18n), allowing you to render templates in different languages based on user preferences or locale settings.
To enable i18n in your Selmer templates, you’ll need to integrate a library like clj-i18n
or tufte
to manage translations. These libraries typically use resource bundles or translation files to store localized strings.
Once you’ve set up your translation infrastructure, you can use translation keys in your templates. Here’s an example using a hypothetical translation function:
<p>{{ t "welcome.message" }}</p>
In this example, t
is a function that retrieves the translated string for the key welcome.message
based on the current locale.
Advanced i18n support includes handling plurals and gender-specific translations. This often involves defining multiple translation keys and using conditional logic to select the appropriate one.
Selmer is a versatile and efficient tool for server-side rendering in Clojure applications. By mastering its syntax, leveraging template inheritance, and supporting internationalization, you can build dynamic, user-friendly web applications that cater to a global audience. As you integrate Selmer into your projects, keep best practices in mind to ensure your templates are maintainable, performant, and easy to understand.