JSX Forge (TS transformer)
A JSX to html template literals TypeScript compiler transformer.
Status: Experimental β successor to
@gracile-labs/babel-plugin-jsx-to-literals.
Built as a native TS compiler plugin (via ts-patch), replacing the previous Babel-based approach.
- Specs β JSX β HTML tagged template compiler
- License
Specs β JSX β HTML tagged template compiler
Note
This is not a JSX runtime. It compiles JSX statically to html tagged
templates at build time.
Think Solid-style compilation, but targeting Lit, Β΅html, or any
compatible tagged template runtime.
All examples below show real transformer output (auto-generated imports included).
JSX Syntax
Basic elements
// Input
const el = <div>Hello</div>;
// Output
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<div>Hello</div>`;
Fragments
const el = <>Hello</>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`Hello`;
Nested elements
const el = (
<div>
<main>
<span>Hi</span>
</main>
</div>
);
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<div>
<main><span>Hi</span></main>
</div>`;
Void elements
XML-style self-closing tags are normalized to HTML-compliant void elements.
const el = (
<>
<br />
<hr />
<img src="#" />
</>
);
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<br />
<hr />
<img src="#" />`;
Non-void custom elements are properly closed:
const el = <my-element></my-element>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<my-element></my-element>`;
Expression children
const name = 'World';
const el = <span>{name}</span>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const nameconst name: "World" = 'World';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<span>${nameconst name: "World" }</span>`;
HTML comments
JSX empty expressions with HTML comment syntax are preserved as real HTML comments.
const el = <div>{/* <!-- My Comment --> */}</div>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<div><!-- My Comment --></div>`;
Regular JSX comments ({/* ... */}) without the <!-- --> markers are
stripped.
Attributes & Bindings
Static attributes
const el = (
<div title="Hello" hidden>
Hi
</div>
);
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<div title="Hello" hidden>Hi</div>`;
Expression attributes
const el = <div title={'Hello'}>Hi</div>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<div title=${'Hello'}>Hi</div>`;
Property binding (_:)
Maps to Litβs .property syntax.
const el = <div _:className={'abc'}>Hi</div>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<div .className=${'abc'}>Hi</div>`;
Event binding (on:)
Maps to Litβs @event syntax.
const el = <div on:click={handler}>Hi</div>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<div @click=${handler}>Hi</div>`;
Boolean binding (bool:)
Maps to Litβs ?attr syntax, wrapping the value with Boolean().
const el = <div bool:disabled={true}>Hi</div>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<div ?disabled=${Booleanvar Boolean: BooleanConstructor
<boolean>(value?: boolean | undefined) => boolean
(true)}>Hi</div>`;
Attribute serialization (attr:)
Wraps the value with JSON.stringify() for SSR-friendly serialization.
const el = <div attr:data={{ a: 1 }}>Hi</div>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<div data=${JSONvar JSON: JSONAn intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.
.stringifyJSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
({ aa: number : 1 })}>Hi</div>`;
Conditional attribute (if:)
Wraps the value with ifDefined() β the attribute is omitted from the DOM when
the value is undefined.
const el = <div if:href={url}>Hi</div>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
import { ifDefinedconst ifDefined: <T>(value: T) => typeof nothing | NonNullable<T>For AttributeParts, sets the attribute if the value is defined and removes
the attribute if the value is undefined.
For other part types, this directive is a no-op.
as $_ifDefinedconst $_ifDefined: <T>(value: T) => typeof nothing | NonNullable<T>For AttributeParts, sets the attribute if the value is defined and removes
the attribute if the value is undefined.
For other part types, this directive is a no-op.
} from 'lit/directives/if-defined.js';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<div href=${$_ifDefined$_ifDefined<any>(value: any): anyFor AttributeParts, sets the attribute if the value is defined and removes
the attribute if the value is undefined.
For other part types, this directive is a no-op.
(url)}>Hi</div>`;
Ref directive (use:ref)
Injects a ref() directive call in-place (as an attribute-less binding).
const el = <main use:ref={myRef}>Hi</main>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
import { refconst ref: (_ref?: RefOrCallback<Element> | undefined) => DirectiveResult<typeof RefDirective>Sets the value of a Ref object or calls a ref callback with the element it's
bound to.
A Ref object acts as a container for a reference to an element. A ref
callback is a function that takes an element as its only argument.
The ref directive sets the value of the Ref object or calls the ref callback
during rendering, if the referenced element changed.
Note: If a ref callback is rendered to a different element position or is
removed in a subsequent render, it will first be called with undefined,
followed by another call with the new element it was rendered to (if any).
// Using Ref object
const inputRef = createRef();
render(html`<input ${ref(inputRef)}>`, container);
inputRef.value.focus();
// Using callback
const callback = (inputElement) => inputElement.focus();
render(html`<input ${ref(callback)}>`, container);
as $_refconst $_ref: (_ref?: RefOrCallback<Element> | undefined) => DirectiveResult<typeof RefDirective>Sets the value of a Ref object or calls a ref callback with the element it's
bound to.
A Ref object acts as a container for a reference to an element. A ref
callback is a function that takes an element as its only argument.
The ref directive sets the value of the Ref object or calls the ref callback
during rendering, if the referenced element changed.
Note: If a ref callback is rendered to a different element position or is
removed in a subsequent render, it will first be called with undefined,
followed by another call with the new element it was rendered to (if any).
// Using Ref object
const inputRef = createRef();
render(html`<input ${ref(inputRef)}>`, container);
inputRef.value.focus();
// Using callback
const callback = (inputElement) => inputElement.focus();
render(html`<input ${ref(callback)}>`, container);
} from 'lit/directives/ref.js';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<main ${/* use:ref */ $_ref$_ref(_ref?: RefOrCallback<Element> | undefined): DirectiveResult<typeof RefDirective>Sets the value of a Ref object or calls a ref callback with the element it's
bound to.
A Ref object acts as a container for a reference to an element. A ref
callback is a function that takes an element as its only argument.
The ref directive sets the value of the Ref object or calls the ref callback
during rendering, if the referenced element changed.
Note: If a ref callback is rendered to a different element position or is
removed in a subsequent render, it will first be called with undefined,
followed by another call with the new element it was rendered to (if any).
// Using Ref object
const inputRef = createRef();
render(html`<input ${ref(inputRef)}>`, container);
inputRef.value.focus();
// Using callback
const callback = (inputElement) => inputElement.focus();
render(html`<input ${ref(callback)}>`, container);
(myRef)}>Hi</main>`;
Style map (style:map)
Wraps the value with styleMap() and renames to style.
const el = <div style:map={{ color: 'red' }}>Hi</div>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
import { styleMapconst styleMap: (styleInfo: Readonly<StyleInfo>) => DirectiveResult<typeof StyleMapDirective>A directive that applies CSS properties to an element.
styleMap can only be used in the style attribute and must be the only
expression in the attribute. It takes the property names in the
{@link
StyleInfo
styleInfo
}
object and adds the properties to the inline
style of the element.
Property names with dashes (-) are assumed to be valid CSS
property names and set on the element's style object using setProperty().
Names without dashes are assumed to be camelCased JavaScript property names
and set on the element's style object using property assignment, allowing the
style object to translate JavaScript-style names to CSS property names.
For example styleMap({backgroundColor: 'red', 'border-top': '5px', '--size': '0'}) sets the background-color, border-top and --size properties.
as $_styleMapconst $_styleMap: (styleInfo: Readonly<StyleInfo>) => DirectiveResult<typeof StyleMapDirective>A directive that applies CSS properties to an element.
styleMap can only be used in the style attribute and must be the only
expression in the attribute. It takes the property names in the
{@link
StyleInfo
styleInfo
}
object and adds the properties to the inline
style of the element.
Property names with dashes (-) are assumed to be valid CSS
property names and set on the element's style object using setProperty().
Names without dashes are assumed to be camelCased JavaScript property names
and set on the element's style object using property assignment, allowing the
style object to translate JavaScript-style names to CSS property names.
For example styleMap({backgroundColor: 'red', 'border-top': '5px', '--size': '0'}) sets the background-color, border-top and --size properties.
} from 'lit/directives/style-map.js';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<div style=${/* map */ $_styleMap$_styleMap(styleInfo: Readonly<StyleInfo>): DirectiveResult<typeof StyleMapDirective>A directive that applies CSS properties to an element.
styleMap can only be used in the style attribute and must be the only
expression in the attribute. It takes the property names in the
{@link
StyleInfo
styleInfo
}
object and adds the properties to the inline
style of the element.
Property names with dashes (-) are assumed to be valid CSS
property names and set on the element's style object using setProperty().
Names without dashes are assumed to be camelCased JavaScript property names
and set on the element's style object using property assignment, allowing the
style object to translate JavaScript-style names to CSS property names.
For example styleMap({backgroundColor: 'red', 'border-top': '5px', '--size': '0'}) sets the background-color, border-top and --size properties.
({ colorcolor: string : 'red' })}>Hi</div>`;
Class map (class:map)
Wraps the value with classMap() and renames to class.
const el = <div class:map={{ active: true }}>Hi</div>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
import { classMapconst classMap: (classInfo: ClassInfo) => DirectiveResult<typeof ClassMapDirective>A directive that applies dynamic CSS classes.
This must be used in the class attribute and must be the only part used in
the attribute. It takes each property in the classInfo argument and adds
the property name to the element's classList if the property value is
truthy; if the property value is falsy, the property name is removed from
the element's class.
For example {foo: bar} applies the class foo if the value of bar is
truthy.
as $_classMapconst $_classMap: (classInfo: ClassInfo) => DirectiveResult<typeof ClassMapDirective>A directive that applies dynamic CSS classes.
This must be used in the class attribute and must be the only part used in
the attribute. It takes each property in the classInfo argument and adds
the property name to the element's classList if the property value is
truthy; if the property value is falsy, the property name is removed from
the element's class.
For example {foo: bar} applies the class foo if the value of bar is
truthy.
} from 'lit/directives/class-map.js';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<div class=${/* map */ $_classMap$_classMap(classInfo: ClassInfo): DirectiveResult<typeof ClassMapDirective>A directive that applies dynamic CSS classes.
This must be used in the class attribute and must be the only part used in
the attribute. It takes each property in the classInfo argument and adds
the property name to the element's classList if the property value is
truthy; if the property value is falsy, the property name is removed from
the element's class.
For example {foo: bar} applies the class foo if the value of bar is
truthy.
({ activeactive: true : true })}>Hi</div>`;
Class list (class:list)
Wraps the value with clsx() and renames to class.
const el = <div class:list={{ active: true }}>Hi</div>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
import { clsxfunction clsx.clsx(...inputs: clsx.ClassValue[]): string as $_clsxfunction $_clsx(...inputs: clsx.ClassValue[]): string } from 'clsx';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<div class=${/* list */ $_clsx$_clsx(...inputs: clsx.ClassValue[]): string ({ activeactive: boolean : true })}>Hi</div>`;
Unsafe HTML ($:html)
Injects unsafeHTML() in the element body. The attribute value becomes the
argument.
const el = <div $:html={'<b>Bold</b>'}>Fallback</div>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
import { unsafeHTMLconst unsafeHTML: (value: string | typeof noChange | typeof nothing | null | undefined) => DirectiveResult<typeof UnsafeHTMLDirective>Renders the result as HTML, rather than text.
The values undefined, null, and nothing, will all result in no content
(empty string) being rendered.
Note, this is unsafe to use with any user-provided input that hasn't been
sanitized or escaped, as it may lead to cross-site-scripting
vulnerabilities.
as $_unsafeHTMLconst $_unsafeHTML: (value: string | typeof noChange | typeof nothing | null | undefined) => DirectiveResult<typeof UnsafeHTMLDirective>Renders the result as HTML, rather than text.
The values undefined, null, and nothing, will all result in no content
(empty string) being rendered.
Note, this is unsafe to use with any user-provided input that hasn't been
sanitized or escaped, as it may lead to cross-site-scripting
vulnerabilities.
} from 'lit/directives/unsafe-html.js';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<div>${$_unsafeHTML$_unsafeHTML(value: string | typeof noChange | typeof nothing | null | undefined): DirectiveResult<typeof UnsafeHTMLDirective>Renders the result as HTML, rather than text.
The values undefined, null, and nothing, will all result in no content
(empty string) being rendered.
Note, this is unsafe to use with any user-provided input that hasn't been
sanitized or escaped, as it may lead to cross-site-scripting
vulnerabilities.
('<b>Bold</b>')}</div>`;
Spread attributes
Spread attributes are resolved at compile time using the type checker. Individual properties are expanded into their corresponding bindings.
const props = { id: 'main' };
const el = <div {...props}>Hi</div>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const propsconst props: {
id: string;
}
= { idid: string : 'main' };
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<div id=${propsconst props: {
id: string;
}
['id']}>Hi</div>`;
Component Model
PascalCase JSX tags are compiled to function calls with a props object.
Children are passed via the "$:children" key as a nested tagged template.
Function components
const MyComp = ({ children }) => <>{children}</>;
const el = <MyComp>Hello</MyComp>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const MyCompconst MyComp: ({ children }: {
children: any;
}) => TemplateResult<1>
= ({ childrenchildren: any }) => htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`${childrenchildren: any }`;
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`${MyCompconst MyComp: ({ children }: {
children: any;
}) => TemplateResult<1>
({
'$:children': htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`Hello`,
})}`;
Components with props
const MyComp = ({ title, children }) => <div title={title}>{children}</div>;
const el = <MyComp title={'yo'}>Hi</MyComp>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const MyCompconst MyComp: ({ title, children }: {
title: any;
children: any;
}) => TemplateResult<1>
= ({ titletitle: any , childrenchildren: any }) =>
htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<div title=${titletitle: any }>${childrenchildren: any }</div>`;
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`${MyCompconst MyComp: ({ title, children }: {
title: any;
children: any;
}) => TemplateResult<1>
({
'$:children': htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`Hi`,
})}`;
Dotted component access
const ns = { Comp: ({ children }) => <main>{children}</main> };
const el = <ns.Comp>Hi</ns.Comp>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const nsconst ns: {
Comp: ({ children }: {
children: any;
}) => TemplateResult<1>;
}
= { Comptype Comp: ({ children }: {
children: any;
}) => TemplateResult<1>
: ({ childrenchildren: any }) => htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<main>${childrenchildren: any }</main>` };
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`${nsconst ns: {
Comp: ({ children }: {
children: any;
}) => TemplateResult<1>;
}
.Comptype Comp: ({ children }: {
children: any;
}) => TemplateResult<1>
({
'$:children': htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`Hi`,
})}`;
Control Helpers
For-each (for:each / repeat)
The <for:each> element inside a .map() call is compiled to Litβs repeat()
directive. The key attribute provides the identity function; children become
the template function.
const el = (
<ul>
{['a', 'b'].map((id) => (
<for:each key={id}>
<li>{id}</li>
</for:each>
))}
</ul>
);
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
import { repeatconst repeat: RepeatDirectiveFnA directive that repeats a series of values (usually TemplateResults)
generated from an iterable, and updates those items efficiently when the
iterable changes based on user-provided keys associated with each item.
Note that if a keyFn is provided, strict key-to-DOM mapping is maintained,
meaning previous DOM for a given key is moved into the new position if
needed, and DOM will never be reused with values for different keys (new DOM
will always be created for new keys). This is generally the most efficient
way to use repeat since it performs minimum unnecessary work for insertions
and removals.
The keyFn takes two parameters, the item and its index, and returns a unique key value.
html`<ol> ${repeat(this.items, (item) => item.id, (item, index) => {
return html`<li>${index}: ${item.name}</li>`;
})} </ol>`
Important: If providing a keyFn, keys must be unique for all items in a
given call to repeat. The behavior when two or more items have the same key
is undefined.
If no keyFn is provided, this directive will perform similar to mapping
items to values, and DOM will be reused against potentially different items.
as $_repeatconst $_repeat: RepeatDirectiveFnA directive that repeats a series of values (usually TemplateResults)
generated from an iterable, and updates those items efficiently when the
iterable changes based on user-provided keys associated with each item.
Note that if a keyFn is provided, strict key-to-DOM mapping is maintained,
meaning previous DOM for a given key is moved into the new position if
needed, and DOM will never be reused with values for different keys (new DOM
will always be created for new keys). This is generally the most efficient
way to use repeat since it performs minimum unnecessary work for insertions
and removals.
The keyFn takes two parameters, the item and its index, and returns a unique key value.
html`<ol> ${repeat(this.items, (item) => item.id, (item, index) => {
return html`<li>${index}: ${item.name}</li>`;
})} </ol>`
Important: If providing a keyFn, keys must be unique for all items in a
given call to repeat. The behavior when two or more items have the same key
is undefined.
If no keyFn is provided, this directive will perform similar to mapping
items to values, and DOM will be reused against potentially different items.
} from 'lit/directives/repeat.js';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<ul>
${$_repeat$_repeat<string>(items: Iterable<string>, keyFnOrTemplate: KeyFn<string> | ItemTemplate<string>, template?: ItemTemplate<string> | undefined): unknown (+2 overloads)A directive that repeats a series of values (usually TemplateResults)
generated from an iterable, and updates those items efficiently when the
iterable changes based on user-provided keys associated with each item.
Note that if a keyFn is provided, strict key-to-DOM mapping is maintained,
meaning previous DOM for a given key is moved into the new position if
needed, and DOM will never be reused with values for different keys (new DOM
will always be created for new keys). This is generally the most efficient
way to use repeat since it performs minimum unnecessary work for insertions
and removals.
The keyFn takes two parameters, the item and its index, and returns a unique key value.
html`<ol> ${repeat(this.items, (item) => item.id, (item, index) => {
return html`<li>${index}: ${item.name}</li>`;
})} </ol>`
Important: If providing a keyFn, keys must be unique for all items in a
given call to repeat. The behavior when two or more items have the same key
is undefined.
If no keyFn is provided, this directive will perform similar to mapping
items to values, and DOM will be reused against potentially different items.
(
['a', 'b'],
(idid: string ) => idid: string ,
(idid: string ) => htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<li>${idid: string }</li>`,
)}
</ul>`;
Literal Flavor Directives
A "use html-*" directive at the top of a file controls which html tag
function is imported.
Default (Lit)
const el = <div>Hello</div>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<div>Hello</div>`;
Server-side rendering
'use html-server';
const el = <div>Hello</div>;
import { htmlfunction html(strings: TemplateStringsArray, ...values: unknown[]): ServerRenderedTemplateA lit-html template that can only be rendered on the server, and cannot be
hydrated.
These templates can be used for rendering full documents, including the
doctype, and rendering into elements that Lit normally cannot, like
<title>, <textarea>, <template>, and non-executing <script> tags
like <script type="text/json">. They are also slightly more efficient than
normal Lit templates, because the generated HTML doesn't need to include
markers for updating.
Server-only html templates can be composed, and combined, and they support
almost all features that normal Lit templates do, with the exception of
features that don't have a pure HTML representation, like event handlers or
property bindings.
Server-only html templates can only be rendered on the server, they will
throw an Error if created in the browser. However if you render a normal Lit
template inside a server-only template, then it can be hydrated and updated.
Likewise, if you place a custom element inside a server-only template, it can
be hydrated and update like normal.
A server-only template can't be rendered inside a normal Lit template.
} from '@lit-labs/ssr';
/** @use html-server */ const elconst el: ServerRenderedTemplate = htmlfunction html(strings: TemplateStringsArray, ...values: unknown[]): ServerRenderedTemplateA lit-html template that can only be rendered on the server, and cannot be
hydrated.
These templates can be used for rendering full documents, including the
doctype, and rendering into elements that Lit normally cannot, like
<title>, <textarea>, <template>, and non-executing <script> tags
like <script type="text/json">. They are also slightly more efficient than
normal Lit templates, because the generated HTML doesn't need to include
markers for updating.
Server-only html templates can be composed, and combined, and they support
almost all features that normal Lit templates do, with the exception of
features that don't have a pure HTML representation, like event handlers or
property bindings.
Server-only html templates can only be rendered on the server, they will
throw an Error if created in the browser. However if you render a normal Lit
template inside a server-only template, then it can be hydrated and updated.
Likewise, if you place a custom element inside a server-only template, it can
be hydrated and update like normal.
A server-only template can't be rendered inside a normal Lit template.
`<div>Hello</div>`;
Signals
'use html-signal';
const el = <div>Hello</div>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResultInterprets a template literal as an HTML template that can efficiently
render to and update a container.
Includes signal watching support from withWatch().
} from '@lit-labs/signals';
/** @use html-signal */ const elconst el: TemplateResult = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResultInterprets a template literal as an HTML template that can efficiently
render to and update a container.
Includes signal watching support from withWatch().
`<div>Hello</div>`;
Auto-Imports
All runtime imports are injected automatically based on usage β you never
import directives manually. The $_ prefix (configurable via
antiCollisionImportPrefix in the preset) prevents naming collisions with user
code.
| Feature used | Auto-imported |
|---|---|
| Any JSX | html from lit (or flavor variant) |
if: | ifDefined from lit/directives/if-defined.js |
use:ref | ref from lit/directives/ref.js |
style:map | styleMap from lit/directives/style-map.js |
class:map | classMap from lit/directives/class-map.js |
class:list | clsx from clsx |
$:html | unsafeHTML from lit/directives/unsafe-html.js |
$:svg | unsafeSVG from lit/directives/unsafe-svg.js |
<for:each> | repeat from lit/directives/repeat.js |
Mix & Match
JSX and tagged template literals are fully interoperable. You can embed html
literals inside JSX and JSX inside html literals.
// Tagged template inside JSX
const el = <main>{html`<span>Inner</span>`}</main>;
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
const elconst el: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<main>${htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`<span>Inner</span>`}</main>`;