From a7fcd732ddf38367dcb1e999f5781675e6e3499a Mon Sep 17 00:00:00 2001 From: Micah Rairdon Date: Sat, 21 Oct 2023 16:07:02 -0400 Subject: [PATCH] How to run multiple React versions using Single Spa --- ...ultiple-react-versions-using-single-spa.md | 73 ++++++++++++++++++ .../multiple-react-versions-yay.png | Bin 0 -> 5553 bytes 2 files changed, 73 insertions(+) create mode 100644 _posts/2023-10-21-how-to-run-multiple-react-versions-using-single-spa.md create mode 100644 images/how-to-run-multiple-react-versions-using-single-spa/multiple-react-versions-yay.png diff --git a/_posts/2023-10-21-how-to-run-multiple-react-versions-using-single-spa.md b/_posts/2023-10-21-how-to-run-multiple-react-versions-using-single-spa.md new file mode 100644 index 0000000..a46a364 --- /dev/null +++ b/_posts/2023-10-21-how-to-run-multiple-react-versions-using-single-spa.md @@ -0,0 +1,73 @@ +--- +published: true +layout: post +title: 'How to run multiple versions of React side-by-side using Single Spa' +description: How to run multiple versions of React side-by-side when using Single Spa +modified: 2023-06-29 +tags: + - Development + - single-spa + - react +categories: + - Web Development +--- + +This seems like it should be easy right? [Single-spa](https://single-spa.js.org/) is a micro-frontend framework +designed to handle running multiple SPA frameworks side-by-side. It's pretty straightforward in all +the other frameworks... but not so in React. Let's dig into why that is. + +## What's the problem? + +In order to use React you always have to install two packages: + +1. `react` - The main runtime for React +2. Some other package that handles the rendering (i.e., `react-dom`/`react-canvas`/`react-native`/`react-pdf`/etc.) + +This allows developers to plug multiple renderers into the React runtime and magically grants everyone who +knows React the ability to write JSX code that works in a bunch of random places. + +Sounds magical! Why is this a problem again? + +The problem only comes when we're trying to work with single-spa. Let's setup a quick project to show how this works: + +1. I create a root config +2. I create a React project running React 17, setting up my webpack externals to look like: `{ react: 'react-17', ['react-dom']: 'react-dom-17' }` +3. I create another React project running React 18, setting up my webpack externals to look like `{ react: 'react-18', ['react-dom']: 'react-dom-18 }` +4. I add import maps for `react-17`, `react-dom-17`, `react-18`, `react-dom-18` all pointing to their various `https://cdn.jsdelivr.net/npm/` urls. + +We boot up the site and we get a nice fat error message: `Unable to resolve bare specifier 'react' from https://cdn.jsdelivr.net/npm/react-dom@17.0.2/umd/react-dom.development.js` + +The reason we get this error message is that `react-dom` was bundled with a dependency on `react`. So, even though +we have our application code swapping out every `react` import with either a `react-17` or a `react-18` import and +we're doing the same for where our application code references `react-dom` we can't do anything about the code inside +the `react-dom` library that is looking for a module named `react-dom`. + +It's a problem unique to React because of it's two package delivery system. If they shipped it as one package like Angular/Vue/etc. +then we wouldn't be having this problem. + +## The solution + +We're going to re-bundle up our own distribution of the `react-dom` libraries. Running it through webpack first. That way +we can tell `react-dom-17` to import `react-17` and we can tell `react-dom-18` to import `react-18`. + +What's the easiest way to do that? + +Let's use `npx create-single-spa` to spin out two `utility modules`: + +1. One to re-export `react-dom@17` and have it's webpack externals set to: `{ react: 'react-17' }` +2. One to re-export `react-dom@18` and have it's webpack externals set to: `{ react: 'react-18' }` + +The only file in both projects will look like this: + +```js +import ReactDOM from 'react-dom' +export default ReactDOM +export * from 'react-dom' +``` + +Now in our import maps we reference our own utility module versions of react-dom instead of the ones hosted on cdn.jsdelivr.net +and things just work! + +{% include image.html path="multiple-react-versions-yay.png" %} + +If you want to check out the working code, [I've got a sample repository on Github](https://github.com/Tiberriver256/multiple-react-versions-single-spa). \ No newline at end of file diff --git a/images/how-to-run-multiple-react-versions-using-single-spa/multiple-react-versions-yay.png b/images/how-to-run-multiple-react-versions-using-single-spa/multiple-react-versions-yay.png new file mode 100644 index 0000000000000000000000000000000000000000..71df4b7ea6b3942a089fdac57eb1951d9bdfb5c8 GIT binary patch literal 5553 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 E0K(wC7ytkO literal 0 HcmV?d00001