Star

Created With

Renderer


Renderer objects provide all of callbag-jsx's core functionalities.

1linkimport { makeRenderer } from 'callbag-jsx';

2link

3linkconst renderer = makeRenderer();




linkCreating Elements

.create() method creates elements or components. JSX is transpiled to renderer.create(), so these two are the same:

1linkconst x = <div class='X'/>;

2linkconst y = <Component prop={42}>Hellow There!</Component>;

1linkconst x = renderer.create('div', {'class': 'X'});

2linkconst y = renderer.create(Component, {'prop': 42}, 'Hellow There!');




linkRendering Elements

.render() method renders given elements on/before/after target elements:

1linkrenderer.render(x).on(document.body);

2linkrenderer.render(<div>Hellow</div>).before(x);

3linkrenderer.render(<span>!</span>).after(x);


👉 When an element is rendered on the main document (appearing on screen) using .render(), its life-cycle hooks will also be activated. This causes all callbags embedded in the element to also be tracked and used to update the element's content / attributes:

1linkconst x = <div>{interval(1000)} have been passed.</div>;

2link

3linkdocument~~~~~~~~.~body~~~~.~appendChild~~~~~~~~~~~(~x~)~;~

4link// 👉 `x` will be rendered on body, but the interval will not be tracked,

5link// so its content won't be updated.

6link

7linkrenderer.render(x).on(document.body);

8link// 👉 `x` will be rendered on body, and the interval will also be tracked,

9link// updating `x`'s content every second.


⚠️ IMPORTANT ⚠️

Always use renderer.render() instead of document.body.appendChild(), or equivalent methods of DOM APIs.




linkRemoving Elements

.remove() function will remove given element from its parents:

1linkrenderer.remove(x);


When an element is removed from the main document using .remove(), its life-cycle hooks will also be cleared. This ensures that subscriptions and other allocated resources for the element will also be cleared.


⚠️ IMPORTANT ⚠️

Always use renderer.remove() for removing elements, as otherwise you will have orphan subscriptions and other resources not cleaned up and sticking in the memory.




linkOther Methods

Renderers also provide the following methods useful for manipulating DOM:

1linkrenderer.append(target, host); // --> appends target to host

2linkrenderer.setProp(node, prop, target); // --> sets given property to target on given node

3linkrenderer.setContent(node, target); // --> sets content (e.g. inner HTML) of given node to given target

4linkrenderer.hook(node, { bind(), clear() });// --> attach life-cycle hooks to the given node

5linkrenderer.leaf(); // --> creates a leaf node (e.g. text node)

6linkrenderer.fragment; // --> creates a fragment (e.g. `DocumentFragment`)


👉 It is highly recommendable to use these methods whenever you want to do DOM manipulations, as they utilize all rendering plugins and additional features otherwise not available. For example, .setProp() can set properties to callbags, etc.




linkRenderer Plugins

The makeRenderer() function returns a DOM Renderer with some callbag-related plugins plugged:

1linkimport { CommonDOMRenderer, LiveDOMRenderer } from 'render-jsx/dom'; // @see [Render JSX](https://loreanvictor.github.io/render-jsx/docs/usage/dom/overview)

2linkimport {

3link CallbagAppendPlugin, CallbagPropPlugin, CallbagContentPlugin, CallbackTrackPlugin,

4link CallbagInputValuePlugin, CallbagInputStatePlugin, CallbagEventHandlerPlugin,

5link CallbagClassPlugin, CallbagStylePlugin,

6link} from 'callbag-jsx/plugins';

7link

8link

9linkexport function makeRenderer(dom?: DOMWindow) {

10link return new CommonDOMRenderer(dom).plug(

11link () => new CallbagAppendPlugin<Node>(), // --> allows appending callbags to other nodes

12link () => new CallbagPropPlugin<Node>(), // --> allows setting node properties to callbags

13link () => new CallbagContentPlugin<Node>(), // --> allows setting `_content` attribute to callbags

14link () => new CallbagTrackPlugin<Node>(), // --> allows components to track callbags on their lifecycle hooks

15link () => new CallbagInputValuePlugin(), // --> allows input values being bound to callbags

16link () => new CallbagInputStatePlugin(), // --> allows input state sync with a callbag state

17link () => new CallbagEventHandlerPlugin(), // --> allows handling events by piping them to callbags

18link () => new CallbagClassPlugin(), // --> allows setting dynamic classes using callbags

19link () => new CallbagStylePlugin(), // --> allows setting dynamic styles using callbags

20link ) as LiveDOMRenderer;

21link}


👉 You can plug these plugins into other JSX renderers (e.g. a native renderer) for enabling callbag support.
👉 You can plug other plugins into your renderers for additional functionality.

Learn More



linkExample: Date Plugin

In this example we create a custom plugin that allows us to render Date objects on the DOM:

index.tsx
1linkimport { makeRenderer } from 'callbag-jsx';

2linkimport { DatePlugin } from './date.plugin'; // @see tab:Plugin Code

3link

4linkconst renderer = makeRenderer().plug(() => new DatePlugin<Node>());

5link

6linkrenderer.render(

7link <>

8link <h1>Hellow!</h1>

9link <p>Today is {new Date()}</p>

10link </>

11link).on(document.body);

date.plugin.ts
1linkimport { AppendPlugin, Plugin } from 'render-jsx/plugin';

2linkimport { RendererLike } from 'render-jsx';

3link

4linkexport class DatePlugin<N>

5link extends Plugin<N, RendererLike<N>>

6link implements AppendPlugin<N, RendererLike<N>> {

7link

8link priority() { return Plugin.PriorityFallback; }

9link

10link append(target: any, host: any) {

11link if (target instanceof Date) {

12link this.renderer().append(target.toDateString(), host);

13link return true;

14link }

15link

16link return false;

17link }

18link}


PlaygroundLearn More



linkExample: Config Plugin

In this example we create a custom plugin that provides given config object to all components:

greetings.tsx
1linkexport function Greetings({ to }, renderer) {

2link return <h1>{this.config.greeting} {to}!</h1>;

3link}

index.tsx
1linkimport { makeRenderer } from 'callbag-jsx';

2linkimport { ConfigPlugin } from './config.plugin'; // @see tab:Plugin Code

3linkimport { Greetings } from './greetings';

4link

5linkconst renderer = makeRenderer()

6link .plug(() => new ConfigPlugin<Node>({

7link greeting: 'Hellow',

8link }));

9link

10linkrenderer.render(

11link <>

12link <Greetings to='World'/>

13link <p>Today is {new Date()}</p>

14link </>

15link).on(document.body);

config.plugin.ts
1linkimport { ComponentProcessor } from 'render-jsx/component';

2linkimport { RendererLike } from 'render-jsx';

3link

4linkexport class ConfigPlugin<N>

5link extends ComponentProcessor<N, RendererLike<N>> {

6link

7link constructor(readonly config: any) { super(); }

8link

9link priority() { return ComponentProcessor.PriorityFallback; }

10link

11link process(provide) {

12link provide({

13link config: this.config

14link });

15link }

16link}


PlaygroundLearn More



linkExample: Class-based Components

In this example we create a plugin that enables use of class-based components:

greetings.tsx
1linkimport { Component } from './class-comp.plugin';

2link

3linkexport class Greetings extends Component<Node> {

4link render(renderer) {

5link return <h1>Hellow {this.props.to}</h1>

6link }

7link}

index.tsx
1linkimport { makeRenderer } from 'callbag-jsx';

2linkimport { ClassComponentPlugin } from './class-comp.plugin'; // @see tab:Plugin Code

3linkimport { Greetings } from './greetings';

4link

5linkconst renderer = makeRenderer()

6link .plug(() => new ClassComponentPlugin<Node>());

7link

8linkrenderer.render(

9link <>

10link <Greetings to='World'/>

11link <p>Today is {new Date()}</p>

12link </>

13link).on(document.body);

class-comp.plugin.ts
1linkimport { RendererLike } from 'render-jsx';

2linkimport { ComponentPlugin } from 'render-jsx/component/plugins';

3link

4link

5linkexport abstract class Component<Node, Renderer=RendererLike<Node>> { // --> a base class for our components

6link static __COMP_CLASS_BASE__ = true; // --> this allows us to check if given tag is a class extending this base class

7link

8link constructor(

9link protected props, // --> collect given props

10link protected children, // --> collect the children

11link protected renderer, // --> collect the renderer

12link protected provision // --> collect additional provisions

13link ) {}

14link

15link abstract render(renderer: Renderer); // --> this will be invoked for rendering stuff

16link}

17link

18link

19linkexport class ClassComponentPlugin<Node> // --> so our plugin is generic towards node type

20link extends ComponentPlugin<Node, RendererLike<Node>> { // --> and can work with any renderer

21link

22link priority() { return ComponentPlugin.PriorityMax; }

23link

24link match(component) { // --> determines if given component data match this plugin

25link return typeof component.tag === 'function' // --> check if the tag is a function (constructor)

26link && component.tag.__COMP_CLASS_BASE__; // --> check if it is a class extending our base component class

27link }

28link

29link createComponent(component, provision) {

30link return new component.tag( // --> invoke the constructor

31link component.props, // --> give it the props

32link component.children, // --> give it the children

33link this.renderer(), // --> give it the plugged in renderer

34link provision // --> give it the additional provisions

35link ).render(this.renderer()); // --> call its render

36link }

37link}


PlaygroundLearn More





Creating ElementsRendering ElementsRemoving ElementsOther MethodsRenderer PluginsExample: Date PluginExample: Config PluginExample: Class-based Components

Home Getting Started


Introductionchevron_right

Basicschevron_right

Reactivitychevron_right

Componentschevron_right

In-Depthchevron_right

Metachevron_right