Designing with Thymeleaf: Previewing Templates in a Browser Without a Server
Thymeleaf’s natural templating feature lets designers open raw HTML files in a browser and see a realistic prototype, while preserving Thymeleaf attributes for later rendering. Learn how to set up natural templates, avoid pitfalls, and keep design and development in sync.
30 Oct 2025, 22:10 UTC

Concrete Problem: Designers Can’t See Thymeleaf Templates Live
In many teams, UI designers work with static HTML files while developers add Thymeleaf expressions later. When a designer opens a raw template in a browser, the th:* attributes are treated as unknown HTML attributes and simply appear in the DOM. The page renders as plain HTML, but the designer has no sense of how the final dynamic page will look once the server injects data. This disconnect can cause mis‑aligned expectations and extra back‑and‑forth communication.
Thesis: Natural Templating Bridges the Gap
Thymeleaf’s *natural templating* mode was designed to solve exactly this issue. When a template file is opened directly in a browser, all attributes—including th:text, th:each, and custom dialect attributes—are treated as inert markup. The browser renders the original HTML content unchanged, providing a realistic prototype. Later, the same file is processed by a Thymeleaf engine on the server, where only the th:* attributes are evaluated. The result is a single source of truth that serves both designers and developers.
How Thymeleaf Handles Unknown Attributes
Thymeleaf’s parser copies every attribute it encounters into the DOM tree. It only evaluates attributes that start with the th: namespace during template execution. All other attributes, including custom ones from other dialects, remain untouched. This behavior means that a raw template is valid HTML and can be viewed in a browser without any special handling.
Setting Up Natural Templates in Your Project
To take advantage of natural templating, you only need to ensure that the template files are served as static resources during the design phase. No special configuration is required on the server side for the preview:
- Create a folder (e.g.,
src/main/resources/templates) and place your HTML files there. - During the design sprint, open the file directly from the filesystem or via a simple static server (e.g.,
python -m http.server). - When the project is built for production, the same files are fed into the Thymeleaf engine via the standard
SpringTemplateEngineorTemplateEngineconfiguration.
Because the files are identical in both stages, designers can iterate on the static markup, and developers can later sprinkle th:* expressions without breaking the preview.
Worked Example: From “Hello World” to Dynamic Greeting
Let’s walk through a minimal example that demonstrates the preview and final rendering.
1. Create the Template
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Greeting</title>
</head>
<body>
<p th:text="${welcomeMessage}">Hello World</p>
</body>
</html>
Open hello.html in a browser. You will see the literal text “Hello World” and the th:text attribute will be invisible. The page looks exactly like a static prototype.
2. Render with Thymeleaf (Java)
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
public class RenderExample {
public static void main(String[] args) {
ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
resolver.setTemplateMode("HTML");
resolver.setPrefix("/templates/");
resolver.setSuffix(".html");
resolver.setCharacterEncoding("UTF-8");
TemplateEngine engine = new TemplateEngine();
engine.setTemplateResolver(resolver);
Context ctx = new Context();
ctx.setVariable("welcomeMessage", "Hi, Thymeleaf!");
String output = engine.process("hello", ctx);
System.out.println(output);
}
}
Running this program prints:
<p>Hi, Thymeleaf!</p>
The th:text attribute is removed, and the value from the context is injected.
3. Verify the Parser Retains Non‑Thymeleaf Attributes
Thymeleaf’s parsing API can be used to inspect the DOM before rendering. For example:
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.templateresolver.StringTemplateResolver;
import org.thymeleaf.standard.StandardDialect;
import org.thymeleaf.templatemode.TemplateMode;
StringTemplateResolver resolver = new StringTemplateResolver();
resolver.setTemplateMode(TemplateMode.HTML);
TemplateEngine engine = new TemplateEngine();
engine.setTemplateResolver(resolver);
engine.setDialect(new StandardDialect());
String template = "<p th:text=\"${msg}\">Hello</p>";
Context ctx = new Context();
ctx.setVariable("msg", "World");
String result = engine.process(template, ctx);
System.out.println(result); // <p>World</p>
The parser keeps all non‑th: attributes unchanged, confirming the natural preview behavior.
Trade‑offs and Limitations
- Hidden Logic in Text: If a designer writes a conditional phrase directly in the visible text (e.g., “Welcome, Admin!”), the preview will show that text, but once rendered the phrase may disappear if replaced by a
th:ifexpression. Designers should avoid embedding logic in the static text that will later be controlled by Thymeleaf. - Dialect Dependencies: Custom dialect attributes (e.g., from LayoutDialect or SpringSecurityDialect) are also inert in the browser. UI elements that rely on those attributes may look different in the preview compared to the final rendered page. Keep the preview simple and add complex dialect logic only after the design is locked.
- Complex Conditional Markup: When using
th:iforth:unlessto hide sections, the preview will still render the hidden markup. Designers may need wrapper comments or placeholder text to keep the preview readable.
Actionable Takeaway
To keep designers and developers aligned:
- Use the same HTML files for both preview and production. Open them directly in a browser during design sprints.
- Keep all dynamic logic in
th:*attributes. Avoid writing conditional content in the static text that will later be replaced. - Document any dialect attributes that affect layout so designers know they may change in the final render.
- When the design is finalized, run a quick Thymeleaf render locally to confirm that the preview matches the output.
By embracing natural templating, teams can reduce miscommunication, speed up UI iteration, and maintain a single source of truth for both design and development.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.