<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/writing-a-thymeleaf-component-library---part-2-ibhbku0a4" -->

---
title: Writing a Thymeleaf component library - part 2 | daily.dev
description: Part 2 of a series on building a Thymeleaf component library for Spring Boot. Covers creating a custom Thymeleaf dialect with a prefix (tcl:) so components can...
canonical: https://daily.dev/posts/writing-a-thymeleaf-component-library---part-2-ibhbku0a4
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Writing a Thymeleaf component library - part 2 | daily.dev
og:description: Part 2 of a series on building a Thymeleaf component library for Spring Boot. Covers creating a custom Thymeleaf dialect with a prefix (tcl:) so components can...
og:url: https://daily.dev/posts/writing-a-thymeleaf-component-library---part-2-ibhbku0a4
og:image: https://api.daily.dev/og/posts/ibhBKu0A4.png
og:image:alt: Writing a Thymeleaf component library - part 2
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Writing a Thymeleaf component library - part 2

**[Wim Deblauwe](https://daily.dev/sources/wim-deblauwe)** · 10 min read · 1 upvotes · 0 comments

## Summary

Part 2 of a series on building a Thymeleaf component library for Spring Boot. Covers creating a custom Thymeleaf dialect with a prefix (tcl:) so components can be used as custom tags like <tcl:button> instead of verbose th:replace fragments. Walks through implementing a ComponentElementProcessor that maps custom tags to fragment templates, building a button component with CSS, enabling live reload of HTML templates via FileTemplateResolver and Vite, and handling arbitrary element attributes via a custom AttrsExceptAttributeProcessor that passes through unhandled attributes (like hx-* for htmx) while merging the class attribute and excluding explicitly handled props like 'primary'.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://www.wimdeblauwe.com/blog/2026/08/03/writing-a-thymeleaf-component-library---part-2>

## Questions this post answers

### How do I pass arbitrary HTML attributes like htmx hx-post through to a custom Thymeleaf component tag?

Create a custom AbstractAttributeTagProcessor (named tcl:attrsExcept in this implementation) that reads the attrs map gathered by the element processor and adds any attribute not explicitly excluded to the final element tag. Attributes explicitly consumed by the component template, such as 'primary', are listed in the exclusion set so they don't leak into the rendered HTML, while the class attribute is merged rather than replaced.

_Developers wiring htmx attributes through Thymeleaf components can browse implementation walkthroughs like this on daily.dev._

### How can I create custom tag-like components in Thymeleaf, such as tcl:button, instead of using th:replace?

Implement a custom Thymeleaf dialect with an IElementModelProcessor that matches all elements with a chosen prefix (e.g. 'tcl') and rewrites them into a th:block with a th:replace fragment expression, such as ~{tcl/components/button :: button}. This makes components discoverable as HTML-like tags and lets you attach arbitrary attributes and content that a plain th:replace on a div cannot support.

_Spring Boot developers designing their own component libraries can find deep dives like this on daily.dev._

### How do I get live reload for Thymeleaf template changes in a component library during development?

Register a FileTemplateResolver bean, enabled via a tcl.dev.templates-path property, that resolves component templates from the file system instead of the classpath with caching disabled and highest precedence. Pairing it with a custom Vite plugin that watches the library's templates directory and triggers a full browser reload lets template edits show up without restarting the Spring Boot app or manually refreshing.

_Developers tuning their Spring Boot dev workflow can track practical setups like this on daily.dev._

## Similar posts on daily.dev

- [Taming Thymeleaf 4th edition release](https://daily.dev/posts/taming-thymeleaf-4th-edition-release-b42gjrxmq) · Wim Deblauwe · 1 upvotes · 0 comments
- [How I Write Custom Elements with lit-html – Frontend Masters Blog](https://daily.dev/posts/how-i-write-custom-elements-with-lit-html-frontend-masters-blog-5wo9pzi23) · Frontend Masters · 3 upvotes · 0 comments
- [Modern Frontend Complexity: essential or accidental?](https://daily.dev/posts/modern-frontend-complexity-essential-or-accidental--yrurnb4gq) · Lobsters · 2 upvotes · 0 comments
- [You're staying on the list](https://daily.dev/posts/you-re-staying-on-the-list-wy90nvyii) · Wim Deblauwe · 0 upvotes · 0 comments
- [Templating in Java Using JTE](https://daily.dev/posts/templating-in-java-using-jte-xdgkherzt) · Baeldung · 1 upvotes · 0 comments

---

Tags: [#java](https://daily.dev/tags/java), [#spring-boot](https://daily.dev/tags/spring-boot)

[View this post on daily.dev](https://daily.dev/posts/writing-a-thymeleaf-component-library---part-2-ibhbku0a4)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Writing a Thymeleaf component library - part 2","url":"https://daily.dev/posts/writing-a-thymeleaf-component-library---part-2-ibhbku0a4","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/writing-a-thymeleaf-component-library---part-2-ibhbku0a4"},"datePublished":"2026-08-03T17:50:27.652Z","dateModified":"2026-09-14T09:07:23.422Z","description":"Part 2 of a series on building a Thymeleaf component library for Spring Boot. Covers creating a custom Thymeleaf dialect with a prefix (tcl:) so components can...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/fd8883d47bb5e17c231a6ec3e5a2472f?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/fd8883d47bb5e17c231a6ec3e5a2472f?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Wim Deblauwe","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Wim Deblauwe","logo":"https://media.daily.dev/image/upload/s--g1qrglrf--/c_limit,w_256/f_auto,q_auto/v1773648779/logos/wim-deblauwe?_a=BAMAMiiu0","url":"https://daily.dev/sources/wim-deblauwe"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/writing-a-thymeleaf-component-library---part-2-ibhbku0a4","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":1},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"java,spring-boot","timeRequired":"PT10M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Wim Deblauwe","item":"https://daily.dev/sources/wim-deblauwe"},{"@type":"ListItem","position":3,"name":"Writing a Thymeleaf component library - part 2"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/writing-a-thymeleaf-component-library---part-2-ibhbku0a4#faq","mainEntity":[{"@type":"Question","name":"How do I pass arbitrary HTML attributes like htmx hx-post through to a custom Thymeleaf component tag?","acceptedAnswer":{"@type":"Answer","text":"Create a custom AbstractAttributeTagProcessor (named tcl:attrsExcept in this implementation) that reads the attrs map gathered by the element processor and adds any attribute not explicitly excluded to the final element tag. Attributes explicitly consumed by the component template, such as 'primary', are listed in the exclusion set so they don't leak into the rendered HTML, while the class attribute is merged rather than replaced. Developers wiring htmx attributes through Thymeleaf components can browse implementation walkthroughs like this on daily.dev."}},{"@type":"Question","name":"How can I create custom tag-like components in Thymeleaf, such as tcl:button, instead of using th:replace?","acceptedAnswer":{"@type":"Answer","text":"Implement a custom Thymeleaf dialect with an IElementModelProcessor that matches all elements with a chosen prefix (e.g. 'tcl') and rewrites them into a th:block with a th:replace fragment expression, such as ~{tcl/components/button :: button}. This makes components discoverable as HTML-like tags and lets you attach arbitrary attributes and content that a plain th:replace on a div cannot support. Spring Boot developers designing their own component libraries can find deep dives like this on daily.dev."}},{"@type":"Question","name":"How do I get live reload for Thymeleaf template changes in a component library during development?","acceptedAnswer":{"@type":"Answer","text":"Register a FileTemplateResolver bean, enabled via a tcl.dev.templates-path property, that resolves component templates from the file system instead of the classpath with caching disabled and highest precedence. Pairing it with a custom Vite plugin that watches the library's templates directory and triggers a full browser reload lets template edits show up without restarting the Spring Boot app or manually refreshing. Developers tuning their Spring Boot dev workflow can track practical setups like this on daily.dev."}}]}
```

