Security News
ESLint is Now Language-Agnostic: Linting JSON, Markdown, and Beyond
ESLint has added JSON and Markdown linting support with new officially-supported plugins, expanding its versatility beyond JavaScript.
The lit-html npm package is a simple, modern, and efficient library for creating and managing HTML templates with JavaScript. It uses JavaScript template literals with embedded HTML markup to render dynamic content in web applications. The library is designed to be lightweight and fast, with a focus on minimizing the amount of work needed to update the DOM when the application state changes.
Dynamic Template Binding
lit-html allows you to bind data dynamically to your HTML templates using JavaScript expressions within template literals. The example code shows how you can insert a variable 'name' into a paragraph element.
`<p>Hello, ${name}!</p>`
Conditional Rendering
You can use JavaScript ternary operators to conditionally render parts of your template. This example demonstrates rendering a different paragraph element based on the truthiness of a 'condition' variable.
`${condition ? html`<p>True</p>` : html`<p>False</p>`}`
Repeating Templates
lit-html provides a straightforward way to render lists or repeat templates by using standard JavaScript array methods like 'map'. In this code, 'items' is an array that is being mapped to a list of 'li' elements.
`${items.map(item => html`<li>${item}</li>`)}`
Event Handling
Event handling in lit-html is done by prefixing the event name with an '@' symbol and assigning a handler function. The example shows a button element that calls the 'handleClick' function when clicked.
`<button @click=${handleClick}>Click me</button>`
Composability
lit-html templates can be composed together to build complex UIs. This example demonstrates how you can combine different template parts, like 'headerTemplate' and 'footerTemplate', to create a complete layout.
`${headerTemplate} ${footerTemplate}`
React is a popular library for building user interfaces. It also uses a component-based model and JSX for templating, which is similar to lit-html's use of template literals. However, React has a larger ecosystem and provides more features out of the box, such as state management and lifecycle methods.
Vue is another popular framework that offers a reactive and composable data model. It uses an HTML-based template syntax that allows you to declaratively bind the rendered DOM to the underlying component state. Vue's approach is more similar to traditional HTML and less JavaScript-centric compared to lit-html.
Svelte is a compiler-based framework that shifts much of the work to compile time, resulting in smaller runtime size and potentially better performance. Like lit-html, Svelte uses a templating syntax that is close to standard HTML but with additional features for reactivity and state management.
HTML template literals in JavaScript
lit-html
lets you describe HTML templates with JavaScript template literals, and efficiently render and re-render those templates to DOM.
const sayHello = (name) => html`<div>Hello ${name}!</div>`;
const container = document.querySelector('#container');
sayHello('Steve').renderTo(container);
// renders <div>Hello Steve!</div> to container
sayHello('Kevin').renderTo(container);
// updates to <div>Hello Kevin!</div>, but only updates the ${name} part
lit-html
has four main goals:
Goal 1 motivate lit-html
's method of creating HTML <template>
s with markers for dynamic sections, rather than the final DOM tree.
For real-world template use-cases, updating existing DOM is just as important as creating the initial DOM. Using JavaScript template literals without a helper like lit-html
makes it easy to create the initial DOM, but offers no help in efficiently updating it. Developer must either manually find dynamic nodes and update them, or re-render the entire tree by setting innerHTML
.
Even previous HTML-in-JS proposals like E4X were only concerned with creating a static DOM tree with expression values already interpolated into the contents. That's again, good for initial rendering and not so good for updates.
lit-html
is able to preserve the static vs dynamic content distinction that JavaScript template literal syntax makes clear, so it can only update the dynamic parts of a template, completely skipping the static parts on re-renders.
This should offer a performance advantage even against VDOM approaches, as most VDOM libraries do not make a distinction between static and dynamic context. The VDOM trees represent the final desired state and then the whole tree is reconciled against a previous state.
Goal 2 drives lit-html
to HTML-in-JS rather than expressions-in-HTML. Any JavaScript expression can be used in a template, from any scope available where the template is defined.
Goal 3 makes tempalte literals an obvious choice over non-standard syntax like JSX.
Goal 4 is partially acheived by leveraging the built in JavaScript and HTML parsers and not doing anything that would impede using them.
lit-html
is very new, under initial development, and not production-ready.
renderTo()
and the Template
class used by extensions.Even without a build configuration, lit-html
minified with babili
and gzipped measures in at less than 1.5k. We will strive to keep the size extremely small.
html
does not return DOM nodes, unlike many other HTML with tagged template literal examples, but returns a TemplateResult
- an object that contains a template and the values from expressions in the template - which can then be used to create or update DOM.
The template is created only the first time html
is called on a particular template literal. On every subsequent call of html
on the same template literal the exact same template is returned, only the values change.
To call html
multiple times on the same template literal, it'll usually be placed in a function:
let count = 1;
const render = () => html`count: ${count++}`;
The template object is based on an actual HTML <template>
element and created by setting it's innerHTML
, utilizing the browser's HTML parser. The HTML is not created by concatenating the string literals expression values, but by joining the literal parts with special markers. The template object finds and remembers the locations of the markers (called "parts"). This makes the system safer from XSS attacks: a value bound to an attribute can't close the attribute, text content is automatically escaped, etc.
When a template is rendered it is cloned along with the part metadata. Values are set via setAttribute()
and textContent
. Some state is stored on the container to indicate that a template was already rendered there. Subsequent renders use that state to update only the dynamic parts, not the entire template instance.
html
:html
is invoked with strings
and values
.
strings
are the string literal parts as a TemplateStringsArray
. The same instance is returned for every evaluation of a particular template literal.
Look up a cached template keyed by strings
, otherwise...
Create a new template:
Join strings
with a special marker, {{}}
. This creates a template string that looks like a Polymer template with empty expressions.
Create a new <template>
element and set its innerHTML
to the generated template string.
Crawl the <template>
contents, looking for markers and remember their location by index in Part
objects. Text-content expressions create new empty text nodes which are used as placeholders.
Return a TemplateResult
with the template and the values.
TemplateResult.renderTo(container)
:Look for an existing TemplateInstance
on container
If an instance exists, check that it's from the same Template
as this TemplateResult
If the instance is from the same Template
, remove its content from container
.
If an instance doesn't exist for the node, create one from the Template
:
Clone the Template
's <template>
contents.
Iterate through the cloned nodes and create new "instance parts" for nodes that have Part
s. An "instance part" is just a {part, node} record.
Update. For every Part
:
If it's an AttributePart
, build up an attibute value then set the attribute.
If it's a NodePart
, get the value (trampoline thunks, render nested templates, etc), then either append a document fragment or set the textContent
.
If this is the first render, append the result DOM to container
.
HTML templates could easily be the basis for component rendering, similar to JSX in React. A component base class can call an instance method that returns a TemplateResult
and then apply it to the shadow root:
class MyElement extends CoolBaseElement {
static get observedProperties() {
return ['message', 'name'];
}
title = `About lit-html`;
body = `It's got potential.`;
render() { return html`
<h1>${this.title}</h1>
<p>${this.body}</p>
`;}
}
lit-html
has basically all of the benefits of HTML-in-JS systems like JSX, like:
There's no need to load an expression parser and evaluator.
Since template literals are evaluated in JavaScript, their expressions have access to every variable in that scope, including globals, module and block scopes, and this
inside methods.
If the main use of templates is to inject values into HTML, this breaks down a major barrier between templates and values.
They're just JavaScript expressions.
In a type-checking environment like TypeScript, expressions are checked because they are just regular script. Hover-over docs and code-completion just work as well.
Template literals preserve case, even though the HTML parser doesn't for attribute names. lit-html
extracts the raw attribute names, which is useful for template syntaxes that use attribute syntax to set properties on elements.
No tooling required. Understood by all JS editors and tools.
lit-html
only re-renders the dynamic parts of a template, so it doesn't produce a VDOM tree of the entire template contents, it just produces new values for each expression. By completely skipping static template parts, it saves work.
JSX requires that the compiler be configured with the function to compile tags to. You can't mix two different JSX configurations in the same file.
The html
template tag is just a variable, probably an imported function. You can have any number of similar functions in the same JS scope, or set html
to different implementations.
JSX translates to function calls, and can't be manipulated on a per-template basis at runtime. lit-html
produces a template object at runtime, which can be further processed by libraries like ShadyCSS.
Because template literals use ${}
as the expression delimiter, CSS's use of {}
isn't interpreted as an expression. You can include style tags in your templates as you would expect:
html`
<style>
:host {
background: burlywood;
}
</style>
`
Anything coercible to strings are supported:
const render = () => html`foo is ${foo}`;
const render = () => html`<div class="${blue}"></div>`;
Because lit-html
templates are parsed before values are set, they are safer than generating HTML via string-concatenation. Attributes are set via setAttribute()
and node text via textContent
, so the structure of template instances cannot be accidentally changed by expression values, and values are automatically escaped.
Attribute parts store both the HTML-parsed name and the raw name pulled from the string literal. This allows extensions, such as those that might set properties on elements using attribute syntax, to get case-sensitive names.
const render = () => html`<div someProp="${blue}"></div>`;
render().template.parts[0].rawName === 'someProp';
A function value is called with no arguments, in a try/catch block to be safe from exceptions:
let data;
const render = () => html`foo = ${_=>data.foo}`;
Here, data.foo
throws because data
is undefined, but the rest of the template renders.
Thunks are trampolined so they can return other thunks.
const items = [1, 2, 3];
const render = () => html`items = ${items.map((i) => `item: ${i})}`;
const items = {
a: 1,
b: 23,
c: 456,
};
const render = () => html`items = ${Object.entries(items)}`;
const header = html`<h1>${title}</h1>`;
const render = () => html`
${header}
<p>And the body</p>
`;
These features compose so you can include iterables of thunks that return arrays of nested templates, etc...
lit-html
is designed to be extended by more opinionated flavors of template syntaxes. For instance, lit-html
doesn't support declarative event handlers or property setting out-of-the-box. A layer on top can add that while exposing the same API, by wrapping the html
tag in a new tag and modifying the result.
This is accomplished by allowing the TemplatePart
s of a template, which are responsible for setting new values into the DOM, to be replaced with new implementations.
Some examples of possible extensions:
lit-html
sets textContent
by default. Extensions could allow setting innerHTML
or injecting existing DOM nodes.lit-html
is less than 1.5k minified and gzipped.
html
html(callSite: TemplateStringsArray, ...expressions: any[]): TemplateResult
html
is a template tag for template literals, which parses the literal as HTML and returns a TemplateResult
.
TemplateResult
TemplateResult
is a class that holds a Template
object parsed from a template literal and the values from its expressions.
Method renderTo(container: Element): void
Renders a TemplateResult
's template to an element using the result's values. For re-renders, only the dynamic parts are updated.
Property template: Template
A reference to the parsed Template
object.
Property values: any[]
The values returned by the template literal's expressions.
Template
Property element: HTMLTemplateElement
Property parts: Part[]
Part
Property type: string
Method update(instance: TemplateInstance, node: Node, values: Iterator<any>): void
In order to support stateful repeat/if like dom-repeat
and dom-if
a value should be able to control it's rendering somewhat. TBD.
Promises and Async Iterables should be supported natively.
If(cond, then, else)
An if-directive that retains the then
and else
instances for fast switching between the two states, like <dom-if>
.
Example:
const render = () => html`
${If(state === 'loading',
html`<div>Loading...</div>`,
html`<p>${message}</p>`)}
`;
Repeat(items, keyfn, template)
A loop that supports efficient re-ordering by using item keys.
Example:
const render = () => html`
<ul>
${Repeat(items, (i) => i.id, (i, index) => html`
<li>${index}: ${i.name}</li>`)}
</ul>
`;
Guard(guardExpr, template)
Only re-renders an instance if the guard expression has changed since the last render.
Since all expressions in a template literal are evaluated when the literal is evaluated, you may want to only evaluate some expensive expressions when certain other values (probably it's dependencies change). Guard
would memoize function and only call it if the guard expression changed.
Example:
const render = () => html`
<div>Current User: ${Guard(user, () => user.getProfile())}</div>
`;
FAQs
HTML templates literals in JavaScript
The npm package lit-html receives a total of 2,264,027 weekly downloads. As such, lit-html popularity was classified as popular.
We found that lit-html demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 14 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
ESLint has added JSON and Markdown linting support with new officially-supported plugins, expanding its versatility beyond JavaScript.
Security News
Members Hub is conducting large-scale campaigns to artificially boost Discord server metrics, undermining community trust and platform integrity.
Security News
NIST has failed to meet its self-imposed deadline of clearing the NVD's backlog by the end of the fiscal year. Meanwhile, CVE's awaiting analysis have increased by 33% since June.