sveltejs / svelte

Cybernetically enhanced web apps
https://svelte.dev
MIT License
78.39k stars 4.1k forks source link

How to lazy load Svelte components into React app #9209

Open rubenberna opened 1 year ago

rubenberna commented 1 year ago

Describe the problem

Hi,

I'm working on a micro services frontend platform, where the main app is built in React and there are multiple widgets, each with its own route. All widgets are so far in React, but I would like to be able to include Svelte widgets too.

Just rendering a Svelte package into React is not a problem, following this setup:

import Component from 'svelte-test'

function SvelteWrapper(Component) {
  return (props) => {
    const svelteRef = useRef();
    useLayoutEffect(() => {
      while (svelteRef.current?.firstChild) {
        svelteRef.current?.firstChild?.remove();
      }
      new Component({
        target: svelteRef.current,
        props,
      });
    }, []);
    return <div ref={svelteRef}></div>;
  };
}

const SvelteApp = SvelteWrapper(Component)

return (
    <BrowserRouter>
        <Routes>
          <Route
            path="/my-svelte-app"
            exact
            element={
              <React.Suspense fallback={<div>Loading...</div>}>
                <SvelteApp/>
              </React.Suspense>
            }
          />
        </Routes>
    </BrowserRouter>
  )

However, part of the architecture is to lazy load all widgets as follows:

const Component = lazy(() => import('svelte-test'))

If this lazy imported component is of type React, it's not a problem, but if it's a Svelte type, passing this component to the SvelteWrapper function fails -- I believe because it's still a promise -- and the console error is:

image

Describe the proposed solution

Easier mechanism to assimilate Svelte components into React or Vue applications, allowing it to work with lazy loading.

Alternatives considered

I created a lazyloader wrapper but to no avail:

const lazyLoader = (importPath) => {
  return new Promise((resolve, reject) => {
    import(importPath).then((module) => {
      resolve(module.default);
    }, reject);
  });
};

const SvelteComponent = lazy(() => lazyLoader('svelte-test'));

<Suspense fallback={<div>Loading...</div>}>
  <SvelteComponent />
</Suspense>

Importance

nice to have

sheijne commented 12 months ago

Seems like all you would need to do to make this work is create the wrapper in the lazy loader:

resolve(SvelteWrapper(module.default));