
lit-html-server
Render lit-html templates on the server as Node.js streams. Supports all lit-html types, special attribute expressions, and most of the standard directives.
Although based on lit-html semantics, lit-html-server is a great general purpose HTML template streaming library. Tagged template literals are a native JavaScript feature, and the HTML rendered is 100% standard markup, with no special syntax or client-side runtime required.
Usage
Install with npm/yarn
:
$ npm install --save @popeindustries/lit-html-server
...write your lit-html template:
const { html } = require('@popeindustries/lit-html-server');
function layout(data) {
return html`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>${data.title}</title>
</head>
<body>
${body(data.api)}
</body>
</html>`;
}
async function body(apiPath) {
const data = await fetchRemoteData(apiPath);
return html`<h1>${data.title}</h1>
<x-widget ?enabled=${data.hasWidget}></x-widget>
<p class="${data.invertedText ? 'negative' : ''}">${data.text}</p>`;
}
...and render:
const { render } = require('@popeindustries/lit-html-server');
render(layout({ title: 'Home', api: '/api/home' }));
Node API
html
The tag function to apply to HTML template literals (also aliased as svg
)
const name = 'Bob';
html`<h1>Hello ${name}!</h1>`;
All template expressions (values interpolated with ${value}
) are escaped for securely including in HTML by default. An unsafe-html
directive is available to disable escaping:
const { unsafeHTML } = require('@popeindustries/lit-html-server/directives/unsafe-html.js');
html`<div>
${unsafeHTML('<span>dangerous!</span>')}
</div>`;
render(template: string|Readable): Readable
Returns the result of the template tagged by html
as a Node.js Readable
stream of markup.
render(html`<h1>Hello ${name}!</h1>`).pipe(response);
renderToString(template: string|Readable): Promise<string>
Returns the result of the template tagged by html
as a Promise which resolves to a string of markup.
const markup = await renderToString(html`<h1>Hello ${name}!</h1>`);
Browser API
lit-html-server can also be used in a browser context:
const { html, renderToString } = require('@popeindustries/lit-html-server/browser.js');
html
The tag function to apply to HTML template literals (also aliased as svg
). See above.
renderToString(template: string|Promise): Promise<string>
Returns the result of the template tagged by html
as a Promise which resolves to a string of markup. See above.
Writing templates
In general, all of the standard lit-html rules and semantics apply when rendering templates on the server with lit-html-server (read more about lit-html and writing templates here).
Template structure
Although there are no technical restrictions for doing so, if you plan on writing templates for use on both the server and client, you should abide by the same rules:
- templates should be well-formed when all expressions are replaced with empty values
- expressions should only occur in attribute-value and text-content positions
- expressions should not appear where tag or attribute names would appear
- templates can have multiple top-level elements and text
- templates should not contain unclosed elements
Expressions
All of the lit-html expression syntax is supported:
html`<h1>Hello ${name}</h1>`;
html`<div id=${id}></div>`;
- boolean attribute (attribute markup removed with falsey expression values):
html`<input type="checkbox" ?checked=${checked}>`;
- property (attribute markup removed):
html`<input .value=${value}>`;
- event handler (attribute markup removed):
const fn = (e) => console.log('clicked');
html`<button @click=${fn}>Click Me</button>`;
Types
Most of the lit-html value types are supported:
-
primitives: String
, Number
, Boolean
, null
, and undefined
note that undefined
handling is the same as in lit-html: stringified when used as an attribute value, and ignored when used as a node value
-
nested templates:
const header = html`<h1>Header</h1>`;
const page = html`
${header}
<p>This is some text</p>
`;
- Arrays / iterables (sync):
const items = [1, 2, 3];
html`<ul>${items.map((item) => html`<li>${item}</li>`)}</ul>`;
html`<p>total = ${new Set(items)}</p>`;
const promise = fetch('sample.txt').then((r) => r.text());
html`<p>The response is ${promise}.</p>`;
Directives
Most of the built-in lit-html directives are also included for compatibility when using templates on the server and client (even though some directives are no-ops in a server context):
guard(expression, valueFn)
: no-op since re-rendering does not apply (renders result of valueFn
)
const guard = require('@popeindustries/lit-html-server/directives/guard.js');
html`<div>
${guard(items, () => items.map((item) => html`${item}`))}
</div>`;
ifDefined(value)
: sets the attribute if the value is defined and removes the attribute if the value is undefined
const ifDefined = require('@popeindustries/lit-html-server/directives/if-defined.js');
html`<div class=${ifDefined(className)}></div>`;
repeat(items, keyfn, template))
: no-op since re-rendering does not apply (maps items
over template
)
const repeat = require('@popeindustries/lit-html-server/directives/repeat.js');
html`<ul>
${repeat(items, (i) => i.id, (i, index) => html`<li>${index}: ${i.name}</li>`)}
</ul>`;
until(promise, defaultContent)
: no-op since only one render pass (renders defaultContent
)
const until = require('@popeindustries/lit-html-server/directives/until.js');
html`<p>
${until(fetch('content.txt').then((r) => r.text()), html`<span>Loading...</span>`)}
</p>`;
when(condition, trueTemplate, falseTemplate)
: switches between two templates based on the given condition (does not cache templates)
const when = require('@popeindustries/lit-html-server/directives/when.js');
html`<p>
${when(checked, () => html`Checkmark is checked`, () => html`Checkmark is not checked`)}
</p>`;
classMap(classInfo)
: applies css classes to the class
attribute. 'classInfo' keys are added as class names if values are truthy
const classMap = require('@popeindustries/lit-html-server/directives/classMap.js');
html`<div class=${classMap({ red: true })}></div>`;
styleMap(styleInfo)
: applies css properties to the style
attribute. 'styleInfo' keys and values are added as style properties
const styleMap = require('@popeindustries/lit-html-server/directives/styleMap.js');
html`<div style=${styleMap({ color: 'red' })}></div>`;
Thanks!
Thanks to Thomas Parslow for the stream-template library that was the basis for this streaming implementation, and thanks to Justin Fagnani and the team behind the lit-html project!