-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
Provide a general summary of the feature here
Use case
We use microfrontends (MFEs) and multiple teams deploy their front-end pieces independently into the same page. To make everything work, React Spectrum is a shared dependency, external for each MFE. That way we ensure that everyone uses the same version of React Spectrum and that providers and contexts are shared. React Spectrum is not bundled in each MFE.
We use SystemJS and import-maps to be able to load Spectrum at a runtime. Here is how our import map looks like:
{
"imports": {
"@adobe/react-spectrum": "https://mfe.static.workfront.com/adobe-react-spectrum/<hash>/@adobe/react-spectrum.js",
"react-aria": "https://mfe.static.workfront.com/adobe-react-spectrum/<hash>/react-aria.js",
"react-stately": "https://mfe.static.workfront.com/adobe-react-spectrum/<hash>/react-stately.js",
"react-aria-components": "https://mfe.static.workfront.com/adobe-react-spectrum/<hash>/react-aria-components.js",
"@internationalized/": "https://mfe.static.workfront.com/adobe-react-spectrum/<hash>/@internationalized/",
"@react-aria/": "https://mfe.static.workfront.com/adobe-react-spectrum/<hash>/@react-aria/",
"@react-spectrum/": "https://mfe.static.workfront.com/adobe-react-spectrum/<hash>/@react-spectrum/",
"@react-stately/": "https://mfe.static.workfront.com/adobe-react-spectrum/<hash>/@react-stately/"
}
}1. How we build Spectrum
We loop through all the components in the monopackages from React Spectrum project and create a Rspack config for each component.
Here is the list of monopackages we scan to create the list of bundles:
@adobe/react-spectrumreact-ariareact-stately
We also add monopackages themselves - @adobe/react-spectrum, react-aria and react-stately.
Additionally, we include packages from @internationalized scope, which are used by React Spectrum components and can be used on their own as well.
And last, we add react-aria-components.
For the each config, every other package from that set is marked as external.
Then, we run Rspack with multiple generated configurations which gives us a SystemJS bundle for each RSP package.
Such approach allows consumers to consume React Spectrum components using both syntaxes mentioned in React Spectrum documentation:
import {Button} from '@adobe/react-spectrum'and
import {Button} from '@react-spectrum/button'2. Problem with monopackages
Syntax like import {Button} from '@adobe/react-spectrum' gets transformed to System.import('@adobe/react-spectrum'). At a runtime, SystemJS resolves @adobe/react-spectrum entry from import map, loads JS file, parses it, finds out the list of dependencies, loads them, and so on. As @adobe/react-spectrum re-exports from all Spectrum packages, that downloads whole React Spectrum, even if we need only single button.
The same problem happens with react-aria and react-stately monopackages.
Solving problem for monopackages
To address described problem with monopackages, we have created a babel transform which converts imports from @adobe/react-spectrum to @react-spectrum/<component> syntax automatically. This plugin is being run before deployment in the MFE deployment pipeline, so people don't need to do anything special to use it.
3. Problem with react-aria-components
Let's say someone does:
import {Text} from '@react-spectrum/text'Everything would be fine, but @react-spectrum/text depends on react-aria-components, which, in its turn, has imports from react-aria and react-stately. As a result, at a runtime we end up downloading whole react-aria-components, react-aria and react-stately. Even worse, we download all that stuff sequentially, because first SystemJS downloads @react-spectrum/text, then discovers it has dependency from react-aria-components, downloads it, discovers it depends on react-aria and react-stately, downloads them, then discovers their dependencies, etc.
🤔 Expected Behavior?
We'd like to see problem described in the 3. Problem with react-aria-components` section solved.
When I use import {Text} from '@react-spectrum/text', I should only get stuff relevant to "Text" component. Currently, use of PURE comments and tree-shaking optimizations in bundlers help with that, but only when you bundle React Spectrum. If you load @react-spectrum/text dynamically, at a runtime, you don't have ways to do tree-shaking, so the code itself should be structured with runtime use-case in mind.
Tell us how the feature should work!
At very least, when Iimport {Text} from '@react-spectrum/text', I should not download code for ColorPicker. Also, if I use DatePicker, I should not download code for Accordion.
😯 Current Behavior
Current approach does not scale for the case when React Spectrum is being loaded at a runtime and is not bundled. Over time, more and more components get added to the build, which makes our application performance worse with each release.
💁 Possible Solution
The possible approach would be splitting react-aria-components into separate packages. That will allow us to build them separately, and download less at a runtime. The obvious candidate for the new package are contexts. Continuing on @react-spectrum/text example, it needs only HeadingContext and useContextProps from react-aria-components. If we create @react-aria-components/contexts package and move these exports there, it will be a tiny one, and the rest of react-aria-components won't be downloaded when we download @react-spectrum/text.
🔦 Context
described above
💻 Examples
No response
🧢 Your Company/Team
Adobe Workfront, Adobe GenStudio, Adobe Analytics, Adobe Site Optimizer
🕷 Tracking Issue
No response