Stamp
<quiet-stamp>
Renders templates with simple expressions, logic, and custom data.
This component lets you "stamp out" repetitive HTML from a single template. Instead of copying and
pasting similar markup, define the pattern once in a <template> element and let the stamp
render it with different values you provide.
Templates support curly brace expressions like {name}, conditional attributes such as
if and unless, and loops with for. Data can be passed through
data- attributes for simple values or embedded JSON for complex structures.
{name}
{tagline}
<div id="stamp__overview"> <!-- Template --> <template id="user-card"> <quiet-card class="user" orientation="horizontal"> <quiet-avatar slot="header" label="{label}" image="{image}"></quiet-avatar> <div class="info"> <h3>{name}</h3> <p>{tagline}</p> </div> <quiet-button slot="footer" if="{canEdit}" icon-label="Edit" pill> <quiet-icon name="edit"></quiet-icon> </quiet-button> </quiet-card> </template> <!-- First stamp --> <quiet-stamp template="user-card" data-name="Meowy McGee" data-tagline="Freedom's just another word for nothing left to lose." data-label="Profile pic" data-can-edit="true" data-image="https://images.unsplash.com/photo-1672487209629-4d52e0c043d0?q=80&w=256&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" ></quiet-stamp> <!-- Second stamp --> <quiet-stamp template="user-card" data-name="Lady Pawington" data-tagline="Professional sunbeam chaser and nap enthusiast." data-label="Profile pic" data-can-edit="true" data-image="https://images.unsplash.com/photo-1516310789627-2ff305829fbb?q=80&w=256&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" ></quiet-stamp> <!-- Third stamp --> <quiet-stamp template="user-card" data-name="Sir Whiskertons III" data-tagline="Living all nine lives to the fullest, one treat at a time." data-label="Profile pic" data-can-edit="false" data-image="https://images.unsplash.com/photo-1569591159212-b02ea8a9f239?q=80&w=256&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" ></quiet-stamp> </div> <style> #stamp__overview { display: flex; flex-direction: column; gap: .5rem; .user { --spacing: 1rem; .info { flex: 1 1 auto; display: flex; gap: .25rem; flex-direction: column; } h3 { font-size: 1.125rem; line-height: 1; margin-block: 0; } p { margin-block-end: 0; } } } </style>
Examples Jump to heading
Writing templates Jump to heading
Start by creating a <template> element with an ID. Inside, write the HTML you want to
stamp out. Use curly brace expressions like {name} as placeholders for dynamic values.
Expressions work in text content and attributes, but not for tag names or other structural parts of the
template.
<template id="greeting"> <p>Hello, {name}!</p> </template>
Curly brackets are optional in special attributes such as for, if, and
unless. For example, you can use if="isActive" instead of
if="{isActive}".
Then add a <quiet-stamp> element and set its template attribute to the
template's ID. The template must already exist in the DOM when the stamp connects. Pass data using
data attributes or
JSON, and the stamp will render the template with your values.
<quiet-stamp template="greeting" data-name="World"></quiet-stamp> <!-- Renders: <p>Hello, World!</p> -->
You can reuse the same template with different data to stamp out multiple instances. However, avoid putting
<script> and <style> tags inside your template, as they'll be
duplicated each time it renders.
Templates are not reactive. Changing a stamp's data after the initial render won't update the content
automatically. To manually rerender, call the renderTemplate() method. This replaces the
content entirely, so any references to previously rendered elements will break.
Passing data with data attributes Jump to heading
To pass data to a stamp, add one or more
data attributes
to it. You can reference the data in the template using expressions that look like
{propertyName}, where propertyName is a
dataset
property name that corresponds to a data attribute you've supplied, e.g. data-first-name can be
referenced in a template using {firstName}.
<!-- Create a template --> <template id="user-template"> <h4 class="name">{name}</h4> <div class="tagline">{tagline}</div> </template> <!-- Stamp it out --> <quiet-stamp template="user-template" data-name="Meowy McGee" data-tagline="Freedom's just another word for nothing left to lose." ></quiet-stamp> <br> <quiet-stamp template="user-template" data-name="Lady Pawington" data-tagline="Sophistication in a silk scarf and sassy whiskers." ></quiet-stamp>
Passing data with JSON Jump to heading
For simple values data attributes work well, but for complex or nested data structures, you can slot a
<script type="application/json"> element inside the stamp. This keeps
everything declarative while allowing proper JSON formatting.
You can access nested properties at any depth using dot notation, e.g.
{company.department.name}. The following example is contrived to demonstrate working with
deeply nested properties.
<template id="cat-hierarchy"> <div class="cat-card"> <h4>{household.name}</h4> <p>Territory: {household.territory.name}</p> <p>Nap Spot: {household.territory.napSpot.name}</p> <p>Current Occupant: {household.territory.napSpot.occupant.name}</p> <p>Occupant's Title: {household.territory.napSpot.occupant.title}</p> </div> </template> <quiet-stamp template="cat-hierarchy"> <script type="application/json"> { "household": { "name": "Whisker Manor", "territory": { "name": "The Sunny Living Room", "napSpot": { "name": "The Forbidden Couch", "occupant": { "name": "Chairman Meow", "title": "Chief Napping Officer" } } } } } </script> </quiet-stamp> <style> .cat-card { padding: 1rem; border: 1px solid var(--quiet-neutral-stroke-default); border-radius: 0.5rem; h4 { margin-block: 0 0.5rem; } p { margin-block: 0.25rem; } } </style>
Unlike JavaScript objects, remember proper JSON requires double quotes around property names, e.g.
{"name": "value"} instead of {name: "value"}.
You can combine data attributes with JSON data. When the same key exists in both, the JSON value takes precedence.
<quiet-stamp template="my-template" data-title="My Title"> <script type="application/json"> { "settings": { ... } } </script> </quiet-stamp>
Boolean attributes Jump to heading
Boolean attributes, such as <button disabled>, are true when the attribute is present and
false when the attribute is absent. Boolean attributes can be denoted in templates using a
? prefix, e.g. ?disabled.
A truthy comparison is done to determine if the attribute should be applied. All values aside from
false, null, undefined, "", NaN,
0, -0, and 0n are considered truthy.
<template id="button-template"> <button ?disabled="{isDisabled}">Click me</button> </template> <quiet-stamp template="button-template" data-is-disabled="false"></quiet-stamp> <quiet-stamp template="button-template" data-is-disabled="true"></quiet-stamp>
HTML expressions Jump to heading
Expressions are HTML-escaped by default, but you can add :html to any content expression to
render the raw, unescaped HTML.
<div>{content:html}</div>
The :html suffix only works in text content, not in attributes. Using it in an attribute will
look for a property literally named content:html.
Using this option can be dangerous! Make sure you trust the included content, otherwise your app may become vulnerable to XSS exploits!
Escaping expressions Jump to heading
To escape an expression, add a \ character immediately before the opening curly bracket.
<div>\{name}</div>
Escaped expressions will not be evaluated and will be rendered as-is minus the backslash.
Conditionals Jump to heading
To show an element based on a truthy value, use the special if attribute with an expression.
The element will only be shown when the expression is truthy.
<div if="someValue"> I will only be shown when someValue is truthy </div>
The inverse of this is the unless attribute, which shows an element when the value is
not truthy.
<div unless="someValue"> I will only be shown when someValue is falsy </div>
Like boolean attributes, all values aside from false, null,
undefined, "", NaN, 0, -0, and
0n are considered truthy.
Loops Jump to heading
To iterate over an array, use the for attribute on an element. The element will be repeated for
each item in the array. Use the as attribute to specify a name for the current item.
The for attribute supports dot notation for nested arrays, e.g.
for="data.users.list".
<template id="tag-list"> <div class="tags" style="display: flex; flex-wrap: wrap; gap: 0.5rem;"> <quiet-badge for="tags" as="tag"> {tag} </quiet-badge> </div> </template> <quiet-stamp template="tag-list"> <script type="application/json"> { "tags": ["Napping", "Zooming", "Knocking Things Off Tables"] } </script> </quiet-stamp>
Inside a loop, you have access to special variables:
{$index}- The zero-based index of the current item{$count}- The one-based index of the current item (human-readable){$key}- The property name when looping over objects (empty for arrays){$length}- The total number of items in the array{$first}- True if this is the first item{$last}- True if this is the last item{$odd}- True if the index is odd (useful for zebra striping){$even}- True if the index is even{$parent}- Access to the parent loop's context (for nested loops)
If you omit the as attribute, the current item will be available as $item by
default.
<template id="loop-variables-demo"> <ol> <li for="cats" as="cat"> {cat} <span if="$first">(first)</span> <span if="$last">(last)</span> </li> </ol> <p>Total cats: {cats.length}</p> </template> <quiet-stamp template="loop-variables-demo"> <script type="application/json"> { "cats": ["Whiskers", "Mittens", "Sir Fluffington"] } </script> </quiet-stamp>
When looping over an array of objects, access their properties using dot notation on the loop variable.
<template id="team-list"> <ul> <li for="members" as="member"> {member.name} - {member.role} </li> </ul> </template> <quiet-stamp template="team-list"> <script type="application/json"> { "members": [ {"name": "Whiskers", "role": "Keyboard Warmer"}, {"name": "Mittens", "role": "Paper Shredder"}, {"name": "Sir Fluffington", "role": "Meeting Disruptor"} ] } </script> </quiet-stamp>
Nested loops are supported. Inner loops can access variables from outer loops, but if the same alias is used, the inner loop's value will take precedence.
In nested loops, you can use {$parent} to access the parent loop's context. This is useful when
you need the parent's loop variables like {$parent.$index} or {$parent.$first}.
For deeply nested loops, chain multiple $parent references like
{$parent.$parent.$first}.
<template id="nested-parent-demo"> <div for="teams" as="team" class="team"> <strong>{team.name}</strong> <ul> <li for="team.players" as="player"> {$parent.$count}.{$count} — {player} </li> </ul> </div> </template> <quiet-stamp template="nested-parent-demo"> <script type="application/json"> { "teams": [ { "name": "Red Team", "players": ["Whiskers", "Mittens", "Luna"] }, { "name": "Blue Team", "players": ["Mochi", "Biscuit", "Noodle"] } ] } </script> </quiet-stamp> <style> .team { margin-block-end: 1rem; ul { margin-block: 0.25rem 0; padding-inline-start: 1.5rem; } } </style>
Loops work well with conditionals. For example, you can show a message when an array is empty:
<template id="user-list"> <ul> <li for="users" as="user">{user.name}</li> </ul> <p unless="users">No users found.</p> </template>
In addition to arrays, you can also loop over an object's properties. The $key variable gives
you each property name while the as alias provides the value.
<template id="cat-stats"> <table class="quiet-striped"> <tr for="stats" as="value"> <th>{$key}</th> <td>{value}</td> </tr> </table> </template> <quiet-stamp template="cat-stats"> <script type="application/json"> { "stats": { "Name": "Whiskers", "Age": "3 years", "Favorite Nap Spot": "Sunny windowsill", "Daily Treats": "5" } } </script> </quiet-stamp>
Replacing on render Jump to heading
If you don't want the <quiet-stamp> element to remain in the DOM after rendering, use the
replace attribute. With this option, the component will stamp out the specified template and
replace itself with the content.
This can be useful for controlling exactly what appears in the DOM, but it can also be confusing for developers who may not understand how the stamped content was generated.
When using replace, calling renderTemplate() again is not possible because the
stamp element is removed from the DOM.
Where did it go?
Go ahead and inspect the DOM. The stamp has been replaced by this content.
<template id="replaceable-template"> <h3 style="margin-block: 0;">Where did it go?</h3> <p> Go ahead and inspect the DOM. The stamp has been replaced by this content. </p> </template> <quiet-stamp template="replaceable-template" replace ></quiet-stamp>
API Jump to heading
Importing Jump to heading
The autoloader is the recommended way to import components but, if you prefer to do it manually, the following code snippets will be helpful.
To manually import <quiet-stamp> from the CDN, use the following code.
import 'https://cdn.quietui.org/v4.0.0/components/stamp/stamp.js';
To manually import <quiet-stamp> from a self-hosted distribution, use the following
code. Remember to replace /path/to/quiet with the appropriate local path.
import '/path/to/quiet/components/stamp/stamp.js';
Properties Jump to heading
Stamp has the following properties that can be set with corresponding attributes. In many cases, the attribute's name is the same as the property's name. If an attribute is different, it will be displayed after the property. Learn more about attributes and properties
| Property | Description | Reflects | Type | Default |
|---|---|---|---|---|
template
|
The ID of the <template> element to use as a stamp.
|
|
string
|
''
|
replace
|
When true, the stamped content will replace the <quiet-stamp> element instead of
being appended to it. This can be useful for controlling exactly what appears in the DOM, but it can
also be confusing for developers who may not understand how the stamped content was generated.
|
|
boolean
|
false
|
Methods Jump to heading
Stamp supports the following methods. You can obtain a reference to the element and call them like functions in JavaScript. Learn more about methods
| Name | Description | Arguments |
|---|---|---|
renderTemplate() |
Processes the associated template and renders it to the DOM. |