Writing HTML
Easier conditionals
html won't render keyword literals - so true, false, null, and undefined all result in an empty string.
const error = false
html`<div>${error && html`<p class="err">${error}</p>`}</div>`;<div></div>Functions are lazy
Functions are only run when the template renders.
html`<aside>${() => RelatedPosts(postId)}</aside>`;Slots
Slots work via parameters. They can be typed with Renderable. A slot can also be a function that returns a Renderable.
import type { Renderable } from '@itsy/html';
const expensiveQuery = () => `I am so bougie!`
const Links = (str: string) => html`<p>${str}</p>`
const Card = ({ title, children, footer }: {
title: string;
children: Renderable;
footer?: () => Renderable;
}) => html`
<section class="card">
<h2>${title}</h2>
${children}
${footer && html`<footer>${footer}</footer>`}
</section>`;
Card({
title: 'Hi',
children: html`<p>body</p>`,
footer: () => Links(expensiveQuery()),
});<section class="card"> <h2>Hi</h2> <p>body</p> <footer><p>I am so bougie!</p></footer> </section>Iterables
All iterables flatten.
html`<ul>${new Set(['a', 'b'])}</ul>`;<ul>ab</ul>Attributes
attrs() renders an object as attributes. true is a bare attribute, false and nullish are left out — except aria-*, draggable, spellcheck and contenteditable, which write "true" and "false" because both are meaningful.
html`<button ${attrs({
type: 'submit',
disabled: false,
hidden: true,
'aria-expanded': false,
style: { '--w': 10, color: null },
})}>Go</button>`;<button type="submit" hidden aria-expanded="false" style="--w:10;">Go</button>aria and data take an object. class takes anything cx() takes.
html`<li ${attrs({
class: ['item', { selected: true, hidden: false }],
aria: { expanded: false, controls: 'menu' },
data: { category: 'x', active: true },
})}>…</li>`;<li class="item selected" aria-expanded="false" aria-controls="menu" data-category="x" data-active>…</li>cx() on its own, for a class attribute written in the markup.
const active = true;
html`<a class="${cx('link', active && 'is-active')}">…</a>`;<a class="link is-active">…</a>Values
Strings are escaped, numbers print, Html is inserted as-is. Objects and Promises throw code 7.
html`<b>${'a < b'}</b> ${42n} ${html`<i>already html</i>`}`;<b>a < b</b> 42 <i>already html</i>Where a value can go
Text and quoted attributes are escaped. URL attributes are also scheme-checked. Inside a tag use attrs(); inside <script>, <style> or a comment use raw(). Unquoted values and on* attributes throw — see Checks.
html`
<p title="${'"quoted"'}">${'<text>'}</p>
<a href="${'javascript:alert(1)'}">blocked</a>
<input ${attrs({ type: 'search' })}>
<script>${raw('let x = 1')}</script>`;<p title=""quoted""><text></p> <a href="about:blank#blocked">blocked</a> <input type="search"> <script>let x = 1</script>Whitespace
Line breaks and the indentation after them collapse to one space. <pre> and <textarea> are left alone. This behavior can be disabled if needed.
html`
<ul>
<li>a</li>
<li>b</li>
</ul>`;<ul> <li>a</li> <li>b</li> </ul>