191 points by palmfacehn 15 hours ago | 10 comments
mholt 14 hours ago
I'm using the <template> tag heavily in Timelinize[0], which has a fairly sophisticated UI that I'm writing in vanilla JS -- not even jQuery. I use a few libraries (for mapping, datetime, and Bootstrap+Tabler for some nice styles), but that's it.

I wrote some boilerplate JS, yes, but I have complete control over my frontend, and I understand how it works, and I don't need to compile it.

Anyway, it works well so far! The <template> tag is a great way to lay out components and then fill them in with simple JS functions.

One nuance is documented in my code comments:

    // Ohhhhhh wow, we need to use firstElementChild when cloning the content of a template tag (!!!!):
    // https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/template#avoiding_documentfragment_pitfalls
    // I spent way too long on this.
    const elem = $(tplSelector);
    if (!elem) return;
    return elem.content.firstElementChild.cloneNode(true);
Once I figured that out, it's been smooth sailing and very powerful.

Oh, but I haven't cleaned up my code at all yet because I was just experimenting/hustling, so it's a bit spaghetti :) If you go looking, don't mind the mess.

---

[0]: https://github.com/timelinize/timelinize - a self-hosted application for bringing together all your data on your own computer and organizing it onto a single timeline: social media accounts, photo libraries, text messages, GPS tracks, browsing history, and more. Almost ready for its debut...

jfagnani 12 hours ago
You should use document.importNode() to clone templates.

Template contents are in a separate document from the main document, which is what makes them inert. importNode() adopts the nodes into the document so they have the correct prototype immediately (which includes custom elements). Otherwise the adopt steps are run when the elements are first attached to the document, which adds another tree walk to the insert/append operation that costs some time.

So:

    document.importNode(elem.content, true);
Then you'll have a DocumentFragment you can pull nodes out of. Or just append the whole fragment.
mholt 12 hours ago
Awesome -- gonna try this. Thanks for the tip! And lit-html looks cool btw.

Update: I'm using importNode() now -- works great too.

mikae1 12 hours ago
Timelinize, just wow. Been wishing for something like this forever. Love the idea!

Regarding the gpx support... Don't want to lead you astray, but, have you seen https://www.sethoscope.net/heatmap/?

mholt 12 hours ago
Thanks! Hopefully it delivers.

I haven't seen that, actually -- looks very nice. We do render a heatmap already, but I'm learning that heatmaps are very tricky to tune to be most useful/interesting. I will try to learn from that project.

mariusor 13 hours ago
I considered vanilla template initially for building my ActivityPub frontend[1], but in the end the sugar that lit-js adds on top of web components seemed worth the file size overhead.

[1] https://github.com/mariusor/oni

jfagnani 12 hours ago
Author of lit-html here.

Yeah, Lit's tagged template literals and render() method are basically a shorthand for making a <template> element, marking the spots where expressions go, cloning the template, then filling in those sports.

freedomben 12 hours ago
Dude, cool project! I considered building something like this a couple years ago as the only way in hell I'd ever use it is if it's open source and self-hosted (thanks so much for using AGPLv3!). I ended up stuck in analysis paralysis, so I'm super impressed :-)
mholt 12 hours ago
Ha, thanks. It's hard, so no shame for being "stuck". I've iterated on this for over a decade at this point. Still more evolution to come... but I really want this for my family.
Lord_Zero 8 hours ago
What's Tabler?
mholt 8 hours ago
It's this: https://tabler.io/admin-template

Still highly unstable between versions, but for being free, it looks really good and is very flexible. I've been pleased with it for this project.

cyanydeez 12 hours ago
It's almost guaranteed this will be used for unwanted surveillance.
mholt 12 hours ago
It doesn't actually get any of your data, let alone anyone else's. It's just an organizer, so please elaborate.
tmalsburg2 42 minutes ago
What problem is this trying to solve and does it actually succeed at solving it? I‘m struggling to see the appeal given that the JS still needs to model the internal structure of the template in order to fill the slots.
jjcm 13 hours ago
Templates are solid and are slightly more performant than including your html-in-js for webcomponents, but that said I really wish there was some sort of web component file format that incorporated:

- The HTML

- The JS

- The CSS

In some sort of a structured way that's easy to load and distribute as a library. I don't like that to use webcomponents the right way, you have to load the logic from an import and then inline the html aspects of it.

jfagnani 12 hours ago
There is a spec issue open for HTML Modules [1] along with a few proposals. The concept needs some motivated champions to see it through. Microsoft has shown some interested in this lately.

There are userland single-file component projects, like my Heximal[2] project. Though Hexiaml specifically doesn't tackle the module format - it requires some kind of HTML Module polyfill.

I don't think the current situation is so bad though. The container format should mostly be irrelevant to consumers of a component. You need to import the component defintion whether that definition is in a .js file or .html file. Once you import it you use it the same.

There should be very few cases where the file format matters. Maybe for use when JS is turned off if HTML-based web components are considered safe to evaluate (their templates might expressive enough to potentially allow attacks).

[1]: https://github.com/WICG/webcomponents/issues/645

[2]: https://heximal.dev/ (Sorry the site is messed up on mobile. I need to fix it!)

epolanski 10 hours ago
Not gonna lie, I like the design of heximal, but by the end of reading the docs I start missing simple php-like templating.
dexwiz 6 hours ago
Salesforce actually had a really nice module format called LWC that does that. Also supports one way binding. Too bad they never championed outside of customer use.
marcosdumay 12 hours ago
Well, turns out you can just add style tags to the template, and they will be scoped there. It's the only way to get local CSS rules.

You can also add script tags wherever you want. They will execute when added to the DOM, but they won't be scoped.

spankalee 11 hours ago
Styles aren't scoped to templates.

You might be thinking of shadow DOM: if you clone your template into a ShadowRoot, then any included styles will be scoped to that ShadowRoot.

blittle 12 hours ago
Shopify heavily uses <template> elements with their Storefront Web Components project, except the templates themselves are passed as a child to the web component, that dynamically queries the data necessary to render the template, before mounting the element to the DOM: https://shopify.dev/docs/api/storefront-web-components
nozzlegear 14 hours ago
I've been using template elements a lot recently to render products on my wife's photography blog. The product data gets loaded from a headless MedusaJS instance, plugged into a template that matches her blog's theme, and then dropped into the page. I specifically wanted to avoid reaching for something like React since the page load is already pretty heavy with the size of her images. Template's been pretty easy to use in that respect.
nine_k 1 hour ago
Frankly, preact is 4 kB, should be peanuts comparing to any image.
mvdtnz 14 hours ago
Reading the first example I'm not really seeing what gains I get from this. It's just as much work to create a table row using the template as doing it from scratch.
anaisbetts 14 hours ago
Of course, the goal of the example is to teach using an example that makes sense logically. Templates can be arbitrarily large and obviously as the template gets bigger, the difference between it and manual `document.createElement` calls gets much larger.
rendaw 55 minutes ago
The difference in what? TBH I don't get the purpose either. You have to use javascript either way, and it's trivial to do make a shorthand for `document.createElement` that sets properties and adds children... maybe 10 lines per project.

I thought it was an optimization, but benchmarking `document.createElement` was significantly faster (firefox).

And then having `template` in your html means that changes to the template/use are less local.

I feel like this is an improvement that only makes sense if you're neck deep in the react world.

tehbeard 14 hours ago
The example is a contrived *simple* example to get the point of it being easily cloneable into DOM nodes you then manipulate.

Maybe a more understandable example for you would be it's use in conjunction with <slot>, shown here: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/...

epolanski 10 hours ago
Can't understand the point of a react/Vue hello world either, they are more verbose than just writing the html.
phartenfeller 13 hours ago
Think of a large template with many attributes like classes and structure. When you want to render this multiple times you just have to update a few things for the unique instance instead of recreating the structure and classes over and over.
palmfacehn 23 minutes ago
This, and separation of concerns. HTML stays in HTML or your preferred SSR template where it belongs.

Another reason I like <template> is the ease of prototyping. A very straightforward approach is to mock up your HTML+CSS with placeholders of what your populated elements might look like. Instead of translating that into JS element generation code, you can simply remove the populated data and enclose the barebones element in a <template> tag.

Back in JS, you might have a simple initializer to add these elements as needed and a function to (re)populate them.

Simple, to the point and no libraries or frameworks required.

Spivak 14 hours ago
I agree, this seems more complicated than just defining your template in JS. You have to use JS to interact with them anyway and it's basically a mapping between some well-known strings and a factory to create a DOM node except less flexible. The advantage seems to be only if you don't interact with your templates in JS and use them inline in your HTML.
dexwiz 14 hours ago
I think it's designed more for web components.
alserio 14 hours ago
I was wondering, are template elements a good place to store json data in the page to be consumed by js?
jfagnani 12 hours ago
No. I would use <script type="-json">

<script> parses its contents as text, whereas <template> parses as DOM. This means you don't have to escape `<`, just `</script>`.

Myself and some browser engineers been working on proposals to allow for inline modules, including JSON, that are importable into other modules via regular import statements.

This is why I recommend the "-json" type - so it doesn't collide with a future native "json" type.

SahAssar 12 hours ago
Why not use a somewhat proper mime type like `<script type="application/mytype+json">` or similar? Seems like your suggestion is not what the spec recommends: https://html.spec.whatwg.org/multipage/scripting.html#attr-s...
jfagnani 12 hours ago
My understanding is that in implementations any unknown type creates a "data block", which is just unprocessed text.'

I wouldn't use application/json just in case browsers start supporting that and it has different semantics than whatever custom thing you might do, causing a webcompat issue when the native feature rolls out.

Although with JSON, it's pretty unlikely that there would be any differing semantics. JSON modules in JS are just JSON blocks with no special additions and no named exports. That's what inline versions would be as well.

SahAssar 11 hours ago
But wouldn't a subtype like `mytype` in my example (`application/mytype+json`) still be be a valid mime-type and still avoid your concerns? I've used these before.
alserio 12 hours ago
Thank you. And that proposal seems really interesting. Can I ask for a link if you happen to have one publicly accessible?
bsmth 14 hours ago
You might be interested in data attributes for this use case https://developer.mozilla.org/en-US/docs/Web/HTML/How_to/Use... although it depends on the shape of the JSON data you had in mind.
alserio 14 hours ago
What do you mean with shape? Are you concerned about escaping and such?
bsmth 13 hours ago
I can imagine if it's deeply nested data, your markup might get complex, but the structure of the HTML doesn't have to mirror the JSON. There's other examples here which might illustrate more how this can look: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/...
alserio 13 hours ago
Thank you. I was looking at a generic way to pass configuration data to custom elements where I cannot know the shape in advance, since another dev can have different requirements. I support data attributes for simple types but i was wondering if allowing arbitraty json objects via other means could be feasible.
hunter2_ 13 hours ago
Since JSON is a subset of JavaScript, you can just use a script element, and including the json mime type certainly doesn't hurt:

    <script type="application/json" id="myJSONData">
      {
        "name": "John Doe",
        "age": 30
      }
    </script>

    <script>
      const data = JSON.parse(document.getElementById('myJSONData').textContent);
      console.log(data.name + " is " + data.age); // John Doe is 30
    </script>
Note: The JSON data must exist literally within the script element. If you try using a src attribute to request it from elsewhere, the response will not be available via textContent or any other mechanism [0], and at that point you just need to fetch/ajax/xhr it.

[0] https://stackoverflow.com/questions/17124713/read-response-o...

lioeters 11 hours ago
About embedding JSON in a script tag, I recently read an article on the risk of a closing </script> tag within the JSON that could break it.

Safe JSON in script tags: How not to break a site - https://sirre.al/2025/08/06/safe-json-in-script-tags-how-not...

As with all untrusted content, depending on where the JSON string comes from, sanitizing the output is worth considering.

yuchi 14 hours ago
As good as a script element with type application/json.
joeframbach 13 hours ago
I wonder if the browser would attempt to validate the contents of a script tag with type json, versus treating it as a blob that would only be validated when parsed/used. And any performance overhead at load time for doing so. Not at a machine at the moment so I can't verify.
alserio 14 hours ago
well one difference is that application/json scripts are still subject to CSP policies
unilynx 14 hours ago
How so? I don't remember ever having seen issues with this. If anything CSP steers you towards this (instead of inline scripts directly assigning to JS variables)
alserio 13 hours ago
I thought I knew but it seems that the CSP story is unclear. I couldn't find an authoritative source for either position
SahAssar 12 hours ago
CSP blocks execution/inclusion, but since json does not execute and any json mimetype will not do execution there is no problem.

Any CSP-allowed other script can read that application/json script tag and decode it, but it is no different than reading any other data it has access to like any other html element or attribute.

alserio 12 hours ago
That makes sense, thank you
Gualdrapo 13 hours ago
For my portfolio (miler.codeberg.page) and the refactor&redesign I'm doing of it (that I hope to finish soon!), I feed some <template>s with data coming from a json that acts like kind of a database. I feel the process of querying nodes and setting their data is still a bit rudimentary in plain JS and it should be a pain in the ass for huge datasets or lots of nodes, but at least with modern JS it is not necessary to fetch the json with a XMLHTTPRequest or something - it can work importing it like a regular JS module, so at least there's that.
BobbyTables2 6 hours ago
What’s the point of special tags?

Isn’t everything just JSON, CSS, JavaScript, and a whole lot of DIV and SPAN elements?

imiric 13 hours ago
There is untapped potential for building modern web sites by leveraging standard web technologies, such as the template element, and sidestepping a whole bunch of bloated abstractions that plague most modern web frameworks. Essentially, we need the simplicity of old school web development married with modern web features.

There are a few modern libraries and frameworks that trend in this direction: Nue, Datastar, Lit. The DX and UX with these is a breath of fresh air.

I've experimented with this approach myself with Miu[1]. It doesn't quite integrate with Web Components nicely yet, but it's almost there. I have some bones to pick with the WC standard, but that's a separate matter.

My wish is that modern web development can eventually not require any 3rd party libraries or frameworks, just like in the early days. Web developers got used to these because of limitations of native web APIs, but the web has grown leaps and bounds since then. Before the modern workflow became standard, it was magical to open a text editor, and have a functioning web page within a few minutes. No dependencies, no build step, no nonsense. Let's bring that back, please.

[1]: https://github.com/hackfixme/miu

jfagnani 12 hours ago
I wrote a couple of blog posts on why we should add native templating to the DOM so we'd need fewer libraries.

The time is right for a DOM templating API: https://justinfagnani.com/2025/06/26/the-time-is-right-for-a...

What should a native DOM templating API look like?: https://justinfagnani.com/2025/06/30/what-should-a-dom-templ...

quectophoton 11 hours ago
I would be very interested in an API similar to the plates[1] library, but with support for event bindings (instead of being just a one-off rendering of plain HTML). My first thought is that it might not be very maintainable, and at best might only be useful for small templates or templates that are heavy on microdata[2].

But there's just something about this type of binding that makes me want to give it a serious chance, at least once, before completely giving up on it.

[1]: https://github.com/flatiron/plates

[2]: https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Mic...

jfagnani 11 hours ago
Plates style isn't what developers expect from modern templating syntaxes. Every popular syntax today supports inline expressions.
troupo 1 hour ago
> and sidestepping a whole bunch of bloated abstractions that plague most modern web frameworks. Essentially, we need the simplicity of old school web development married with modern web features.

If you listen to people actually building frameworks (Solid, Preact, Vue, Svelte), you'll learn that "modern web features" have very few actual features that make development easier. All frameworks would absolutely love if they could replace "bloated abstractions" with actual built-in browser features. Too bad, all of those features are built in complete isolation from what anyone but a very small group of Chrome devs want.

> Web developers got used to these because of limitations of native web APIs, but the web has grown leaps and bounds since then.

It hasn't. At least not in the direction you want.

> No dependencies, no build step, no nonsense. Let's bring that back, please.

Nothing stops you from doing that now.

EGreg 14 hours ago
What exactly is the point of <template> tag? Just to create document fragments? I mean that’s all you can really do with it in HTML. If you are using JS you can attach shadow dom to any element.

I guess maybe slots for web components is the other point?

acdha 13 hours ago
I’ve used it to have the server generate fragments not processed in the normal page load which are then used as templates by JavaScript which populates them. That supported fast initial page loads compared to an SPA while being just as dynamic after being loaded.

This worked pretty well but definitely had some rough edges which I really wish the standards process could’ve focused on. Things like JSX are so much slower / memory intensive but people use them for convenience and it’d be nice to improve the ease-of-use story.

mikojan 1 hour ago
Could you not achieve the same with a <div hidden>?
dspillett 11 hours ago
> Just to create document fragments?

Largely, I think. Though I've not got around to actually using them yet myself, so take my understanding with a pinch of salt.

Compared to doing that with non-displayed HTML tags outside a template there are performance benefits (the parser deals with it differently knowing it isn't intended to be an active bit of DOM yet) and convenience benefits (IIRC the contents are not going to be picked up by querySelector/querySelectorAll).

Compared to building document fragments in JS they can be a lot more convenient to create and edit, and keep your design and code less tightly coupled (so a designer on your team who doesn't want to touch JS (or who you don't want messing with your JS!) can easily play with them to a certain extent). Other methods to achieve the same separation may involve an extra build step in deploying your work.

65 6 hours ago
Alpine.js uses <template> tags for if statements and for loops with stateful data. E.g. <template x-if="myVariable === 1"><div>1</div></template>

Which is a simpler way of thinking about templates, rather than slots and fragments.

vlad1719 6 hours ago
[dead]
Theodores 13 hours ago
Good question. Yes there was all that web components stuff that didn't go the HTML5 way, and the template element is definitely a child of that. Since elements that get into the spec can't be removed easily, they do get repurposed instead over time.