How to create preact multiple embeddable widget?

Viewed 1107

How to expose a function that can render a preact app where I can pass render position and props?

There is no webpack config can be edited in preact, so how to achieve this?

I've seen preact-habitat plugin, the widget can't be rendered by passing props to a function.

For my use case I need to create a bundle.js that contains multiple embeddable widgets and preact source.

I can call renderWidgetA(position, props), renderWidgetB(position, props) to render it to position, also I need to pass different props to the existing rendered widget

Is there any way to achieve this?

This is a similar logic with svelte: How to expose a function in svelte that can accept parameters to render?

Highly appreciate for any feedback.

1 Answers

Assuming that by position, you mean Mounting HTML element like body, div or some CSS selector like '#mountElm', then you can export a render function like:

import { render, h } from 'preact';

// WidgetA is a Preact component.
// It could be functional or class component.
export function WidgetA(props) {

    // Use props here

    return (
        <div>WidgetA</div>
    );
}


// Render function for WidgetA
export function renderWidgetA(position, props) {

    const renderElm = position instanceof HTMLElement
        ? position
        : document.querySelector(position);

    render(h(Component, props), renderElm);
}

When this file is bundled, it can be simply used like:

import { renderWidgetA } from './my-bundle.js';

renderWidgetA('body', { title: 'Hello world' });

You can follow a similar pattern to create multiple components. Further, you can simply extract the render function into a more generic variant.

Question 1: How to make the function available on the global window object?

To achieve, this you have two options:

  1. Use Webpack and specify libraryTarget for the output. It could be umd or similar. Read more here.
  2. If you cannot use Webpack or forbidden from changing it, then at the end of the file, you can explicitly bind it to a global object.

Example:

window.myLib = { renderWidgetA };

// Usage
myLib.renderWidgetA('body', { /* some props */});

Question 2: What to do when a prop changes for a certain widget.

You have to make a change to the render function. It accepts the optional third parameter that you can set to true. So your function becomes:

export function renderWidgetA(position, props) {

    const renderElm = position instanceof HTMLElement
        ? position
        : document.querySelector(position);

    // NOTE THE THIRD PARAM
    render(h(Component, props), renderElm, true);
}

Once, you do that simply call renderWidgetA function whenever your props change. It is completely harmless. Just make sure that your position node remains the same across multiple re-rendering. Also, always remember that UI as a Function of State. VDOM will take of everything else for you.

Related