Renderer

The base class for creating renderers that convert Crank elements into target-specific output.

Syntax

class Renderer<
TNode extends object,
TScope,
TRoot extends TNode | undefined = TNode,
TResult = ElementValue<TNode>
> {
constructor(adapter: Partial<RenderAdapter<TNode, TScope, TRoot, TResult>>);

render(
children: Children,
root?: TRoot,
bridge?: Context
): Promise<TResult> | TResult;

hydrate(
children: Children,
root: TRoot,
bridge?: Context
): Promise<TResult> | TResult;
}

Type parameters

Constructor

new Renderer(adapter)

Parameters

Instance methods

render()

render(children, root?, bridge?): Promise<TResult> | TResult

Renders an element tree into a root container.

Parameters:

Returns: The rendered result, or a promise if the tree renders asynchronously.

hydrate()

hydrate(children, root, bridge?): Promise<TResult> | TResult

Hydrates server-rendered content by attaching event handlers and reconciling with existing nodes.

Parameters:

Returns: The hydrated result, or a promise if hydration is asynchronous.

Description

Renderer is an abstract class that must be subclassed with a custom RenderAdapter to target specific rendering environments. Crank provides two built-in renderers:

The renderer maintains a cache of element trees per root, enabling efficient updates when re-rendering.

Examples

Using built-in renderers

import {renderer} from "@b9g/crank/dom";

function App() {
return <div>Hello, World!</div>;
}

// Render to DOM
renderer.render(<App />, document.getElementById("root"));

// Update
renderer.render(<App name="Updated" />, document.getElementById("root"));

// Unmount
renderer.render(null, document.getElementById("root"));

Server-side rendering

import {renderer} from "@b9g/crank/html";

function App() {
return <div>Hello, World!</div>;
}

const html = await renderer.render(<App />);
console.log(html); // "<div>Hello, World!</div>"

Creating a custom renderer

import {Renderer} from "@b9g/crank";
import type {RenderAdapter} from "@b9g/crank";

interface CanvasNode {
type: string;
props: Record<string, any>;
children: CanvasNode[];
}

const canvasAdapter: RenderAdapter<CanvasNode, void> = {
create({tag, props}) {
return {type: tag as string, props, children: []};
},
patch({node, props}) {
Object.assign(node.props, props);
},
arrange({node, children}) {
node.children = children;
},
text({value}) {
return {type: "text", props: {value}, children: []};
},
// ... implement other methods
};

class CanvasRenderer extends Renderer<CanvasNode, void> {
constructor() {
super(canvasAdapter);
}
}

Bridging renderers

import {renderer as domRenderer} from "@b9g/crank/dom";
import {renderer as htmlRenderer} from "@b9g/crank/html";

function* App() {
// This component's context can be passed to other renderers
for (const props of this) {
yield <div>...</div>;
}
}

// The bridge parameter connects event propagation and context
const bridge = /* get context from a component */;
htmlRenderer.render(<ChildApp />, undefined, bridge);

See also

Edit on GitHub