Dynamically load React component library from URL?

4.6k Views Asked by At

I am working on documentation tool for Typescript library. The idea is to leverage parcel's watch mode to continuously build the library, and use the same in a pre-built documentation app.

For the same I need to load a module library (built in another project) dynamically via URL.

<script type="module">
    const libraryModule = "http://localhost:8080/lib.module.js";
    const promise = import(libraryModule);

    promise.then(library => {
        // do something with library
        window.ComponentLibrary = library;
    });
</script>

However, parcel replaces the above import with require and the load fails. Using System.import throws System is not defined error.

I tried to use dynamic-import-polyfill and then initialize it as under and the use as below:

dynamicImportPolyfill.initialize({
    modulePath: 'http://localhost:13090', // Defaults to '.'
    importFunctionName: '$$import' // Defaults to '__import__'

    const promise = $$import(libPath);
});

This throws the following error:

TypeError: Failed to resolve module specifier "react/jsx-dev-runtime". Relative references must start with either "/", "./", or "../"

I have also tried using script type as text/javascript but doesn't work either.

Looking for guidance on the best way here to get the component library loaded?

1

There are 1 best solutions below

1
On BEST ANSWER

Figured it out: yes, we can load a component library as a module dynamically.

The issue was that React UMD module is not a pure ES/Javascript module. Also, with React 17, JSX components are picked from react/jsx-runtime. So, first I had to convert the React UMD module into an ES module - it's just a thin wrapper. Similarly, added a wrapper for jsx-runtime. To make things work had to use importmaps which are currently not supported in all browsers - see caniuse.com to check latest support.

This completes your setup and now your library compiled as ES module will work just fine. Below is what I used to get working:

<script type="importmap">
        {
            "imports": {
                "react/jsx-runtime": "/react-jsx-runtime.js",
                "react": "/react-as-es-module.js"
            }
        }
    </script>
    <script type="module" src="/component-library.js"></script>
    <script type="module">
        import * as MyComponentLib from "/component-library.js";
        window.ComponentLibrary = { ...MyComponentLib };
    </script>

Code for react-jsx-runtime.js looks as under:

import * as React from 'react';

export const jsx = React.createElement;
export const jsxs = React.createElement;

Code for react-as-es-module.js goes as:

import 'https://unpkg.com/[email protected]/umd/react.production.min.js';

const {
    Children,
    Component,
    Fragment,
    // and all other exports
} = React || {};

export {
    Children,
    Component,
    Fragment,
    // and all other exports
}

export default React;

I compiled component-library.js using ParcelJS using the type: "module" in package.json file. I would detail this in blog post and demo Github repo soon.

Hope this helps.