Rendering EnginesAPI reference for registering a rendering engine in Semantic UIplugAPI Reference
Categories

Rendering Engines

An engine is a plain object with three keys, registered under a name. The framework calls into it at two points: once when a component is defined, and once when an instance initializes.

Key Type Called by
factory function defineComponent, to build the custom element class
renderer class Template.initialize(), to render on the client
serverRenderer class Template.initialize(), to render to a string. Optional

Without a serverRenderer an engine falls back to its client renderer on the server, which is why lit omits one.

Registration

registerEngine

registerEngine(name, engine);

Adds an engine to the registry.

Parameters

Name Type Description
name string The value renderingEngine matches against
engine object An object with factory, renderer, and optionally serverRenderer

Usage

import { registerEngine, Renderer } from '@semantic-ui/renderer';
import { createComponent } from './factory.js';
registerEngine('native', {
factory: createComponent,
renderer: Renderer,
});

Registration is a side effect of importing the module that calls it. @semantic-ui/component registers native on import, and @semantic-ui/component/server adds the engine’s serverRenderer when it loads, so a browser bundle carries no server renderer. The Lit engine registers only when you import it:

import '@semantic-ui/component/engines/lit/register.js';

getEngine

getEngine(name);

Returns the registered engine, or undefined.

Selecting an Engine

defineComponent({
tagName: 'my-card',
template,
renderingEngine: 'lit',
});

The name resolves once, at definition time. renderingEngine also accepts an engine object, which skips the registry. An unregistered name is fatal rather than silent, since the alternative is a component that renders nothing.

Two components on the same page can use different engines. Every stage before rendering is shared.

Adapter Touchpoints

factory

Called once per component definition. It receives the shared configuration and returns a class for customElements.define.

factory({
prototypeTemplate, // the Template every instance clones from
resolvedProperties, // property definitions, for observedAttributes
css,
delegatesFocus,
componentSpec,
defaultSettings,
plural,
onAttributeChanged,
renderingEngine,
});

The native factory returns a class extending HTMLElement. The Lit factory returns one extending LitElement. What the class extends is the engine’s decision, so an engine targeting something other than the DOM defines what its “element” means.

renderer

Constructed once per instance, during Template.initialize().

new RendererClass({
ast, // compiled template, shared across instances
data, // the flat data context, with settings overlaid as signals
template, // the owning Template
subTemplates,
helpers,
receivesData, // true when the template is a subtemplate
});

The instance must expose render(). The native renderer returns a DocumentFragment, the Lit renderer a TemplateResult. Template stores whatever comes back and does not inspect it.

Shared Modules

An engine writes the walk and the binding. Everything below is exported from @semantic-ui/renderer so it does not have to write these too.

Expressions

ExpressionEvaluator resolves every expression form the template syntax accepts, so an engine does not reimplement the lookup cascade or the Lisp-style argument walk.

import { ExpressionEvaluator } from '@semantic-ui/renderer';

HTML assembly

buildHTMLString turns an AST into one HTML string with markers at every dynamic position, paired with an entries array describing each marker. analyzePosition classifies a marker as attribute or text, boolean or quoted, property or event.

Both are pure and shared between the client and server renderers. An engine that produces a string benefits most; one that builds nodes directly may skip them.

Keyed list identity

{#each} is keyed. It reconciles by an item’s key rather than its index, so a row keeps its DOM node, its focus, and its per-item state across a reorder or a splice. The key is inferred from the item rather than declared in the template.

These resolve identity the same way the shipped engines do, so an engine reconciles a list identically without reimplementing the rules.

Export Purpose
getItemId Resolves an item’s key, falling back through id, _id, hash, key, then index
getCollectionType Classifies the iterated value as array, object, or primitive list
getEachData Builds the per-item data context, including index and alias
encodeItemKey / decodeItemKey Round-trips a key through a DOM-safe string
lisIndices Longest increasing subsequence, for the minimum set of moves in a reorder

Error handling

setRecovery and setTracing control what happens when an expression throws. isRecovery and isTracing read the current state, which an engine checks before deciding whether to swallow an error or let it escape.

Writing an Engine

The two shipped engines are worked examples of the same contract. Native assembles an HTML string, parses it once, then binds markers. Lit accumulates static strings and directive instances, then hands both to lit-html.

Previous
Renderer
Next
Native Renderer