Export component (compiled) for external websites use (with ReactJS)

Viewed 66

My problem started when I wanted to use a component as a widget in an external website. First of all I created a route with react router to print only that component, so I can use it in an iframe, but the problem was that the component has dropdowns so when they were opened you had to scroll in order to access them.

So I did a research on internet to how to make a component "widgeable", but I could not find anything, or at least anything clear.

And here comes my question, is there a way to export my component, with styles and everything, separately from the whole project to use it as a widget for external websites? Is there an standard way to do it?

Also, is there a way to compile with webpack a component? If it is possible, how can I use it after being compiled in an external website?

In order to give you more information, my component its a form, that form has a dropdown calendar and popups, and with the react-router I've created a route on /widgets/form where prints only the form component.

Thanks for reading me!

Update 1

I would like to avoid adding react to the external website, I do want them to simply add an snippet of JS to their body, like this:

((d, s, u) => {
    const h = d.getElementsByTagName(s)[0],
        j = d.createElement(s);
    j.async = true;
    j.src = 'http://localhost:3000/files/widgets/ibb-form.min.js';
    j.onload = () => {
        console.log('Widget: ibblue form loaded!');
        start({ url: u });
    };
    h.parentNode.insertBefore(j, h);
})(document, 'script', 'http://localhost:3000/widgets/form');

function start({ url }) {
    new window.IBBForm({ url });
}

and the JS that I use in the script that snippet adds it's the following one:

(() => {
    const IBBForm = class {
        constructor({ id, url }) {
            this._elementID = id || '#ibblue_form';
            this._target = document.querySelector(this._elementID);

            if (!url) throw new Error('No URL specified');
            this._url = url;

            this._iframe = this._createIframe();
            this._target.append(this._iframe);
        }

        _createIframe() {
            const iframe = document.createElement('iframe');
            iframe.id = 'ibblue_form_iframe_' + new Date().getTime();
            iframe.src = this._url;
            iframe.style =
                'width: 100%; height: 600px; border: medium none; background-color: transparent; display: initial;';
            iframe.allowTransparency = 'true';
            return iframe;
        }
    };

    window['IBBForm'] = IBBForm;
})();

Update 2

I edited the script downloaded to the following one:

import BookingSearch from './../../src/components/sections/BookingSearch';

(() => {
    const IBBForm = class {
        constructor({ id, url }) {
            this._elementID = id || '#ibblue_form';
            this._target = document.querySelector(this._elementID);

            if (!url) throw new Error('No URL specified');
            this._url = url;

            this._addRequirements().then(() => {
                this._element = this._createForm();
            });
        }

        async _addRequirements() {
            await this._addScript('https://unpkg.com/react@17/umd/react.production.min.js');
            await this._addScript('https://unpkg.com/react-dom@17/umd/react-dom.production.min.js');
        }

        async _addScript(url) {
            return new Promise((resolve, reject) => {
                const h = document.getElementsByTagName(s)[0],
                    j = document.createElement(s);
                j.async = true;
                j.type = 'module';
                j.src = url;
                j.crossorigin = '';
                j.onload = resolve();
                h.parentNode.insertBefore(j, h);
            });
        }

        _createForm() {
            return ReactDOM.render(<BookingSearch widget={true} dataTheme={{}} />, this._target);
        }
    };

    window['IBBForm'] = IBBForm;
})();

When I execute it, console throws some errors:

Uncaught SyntaxError: expected expression, got '<'
Uncaught TypeError: window.IBBForm is not a constructor
2 Answers

By creating an iframe with the route to the "widget" (/widgets/form) solves pretty much the problem, the idea was to serve a built js of the react widget but that was not possible.

I could also print the dom of the widget into the website, but I rejected that option for some internal reasons.

Thanks to all you commented and helped me to reach that decision!

Related