Rendering
For rendering, Thunderous exports a tagged template literal function called html``. This
doesn't create a virtual DOM, but instead it directly converts an HTML string into a native
DocumentFragment that can be appended to the DOM. Additionally, it enables template binding
with event listeners and
signals.
Note that Thunderous does not sanitize the input of the html`` function. It is up to the
developer to ensure that the input is safe to render. Please use a package like
DOMPurify or similar to sanitize any dangerous
input.
It's important to remember that Thunderous will only run the component function once, when the custom element
gets upgraded. This is different from other libraries that re-render the component on every state change.
Instead, Thunderous relies on signals to make fine-grained updates to the DOM when the state changes, but more
on that later.
For conditionals, you can easily render nested templates with the html`` function.
import { customElement, html } from 'thunderous';
const MyElement = customElement(() => {
const cond = true;
return html`
<p>
${cond ? html`<span>True</span>` : html`<span>False</span>`}
</p>
`;
});
Short-circuit evaluation is not recommended because false values may be rendered as text.
Ternaries are generally a better choice for conditionals, as they provide more explicit control over the
rendered output. That said, null or undefined will render nothing.
For loops, you can map an array to a list of DocumentFragment objects. Each element should
have a unique key attribute to help Thunderous keep track of the items in the list.
import { customElement, html } from 'thunderous';
const MyElement = customElement(() => {
const list = [{ id: 1, text: 'Item 1' }, { id: 2, text: 'Item 2' }];
return html`
<ul>
${list.map((item) => html`<li key="${item.id}">${item.text}</li>`)}
</ul>
`;
});
To render a reactive template, you can use derived() signals. This will hand off the signal
to html``, which will then update the DOM when the signals change.
For more information on signals, see the signals overview and
derived signals documentation.
import { customElement, html, derived, createSignal } from 'thunderous';
const MyElement = customElement(() => {
const [cond, setCond] = createSignal(true);
const [list, setList] = createSignal([{ id: 1, text: 'Item 1' }, { id: 2, text: 'Item 2' }]);
return html`
<p>
${derived(() => cond() ? html`<span>True</span>` : html`<span>False</span>`)}
</p>
<ul>
${derived(() => list().map((item) => html`<li key="${item.id}">${item.text}</li>`))}
</ul>
`;
});
In the same way, you can use derived() to render reactive attribute values.
import { customElement, html, derived, createSignal } from 'thunderous';
const MyElement = customElement(() => {
const [cond, setCond] = createSignal(true);
return html`
<button class="primary ${derived(() => cond() ? 'primary--active' : '')}">
Click me
</button>
`;
});
// alternatively, for a cleaner template...
const MyElement2 = customElement(() => {
const [cond, setCond] = createSignal(true);
const activeClass = derived(() => cond() ? 'primary--active' : '');
return html`
<button class="primary ${activeClass}">
Click me
</button>
`;
});