-
Notifications
You must be signed in to change notification settings - Fork 38
Version conflicts
As more and more packages are built on top of iTwinUI, it is natural that applications may have multiple such dependencies as well as a direct dependency on iTwinUI. This can cause many headaches if proper care is not taken when designing and consuming such packages. Here are some guidelines and best practices to help you with this:
- Applications should dedupe their packages so that in the end the lockfile should contain only one instance of iTwinUI per major version. To achieve this, you might need to use npm overrides/pnpm overrides/yarn resolutions.
- Applications should migrate to v3 and explicitly import iTwinUI styles in the application entrypoint. This, in addition to using (p)npm overrides to dedupe dependencies, should prevent most issues with version conflicts.
- An application using iTwinUI v3 can still have dependencies that use iTwinUI v2. This scenario should work without any issues, as long as the latest v2 patch is forced, using code similar to below:
"pnpm": { "overrides": { "@itwin/itwinui-react@^2": "^2.12.26", "@itwin/itwinui-react@^3": "^3.10.1" } }
- An application using iTwinUI v3 can still have dependencies that use iTwinUI v2. This scenario should work without any issues, as long as the latest v2 patch is forced, using code similar to below:
- Packages should consider specifying
@itwin/itwinui-react
as a peer dependency, to make it easier for applications to control the exact version. However, this should be done thoughtfully. Using peer deps means the application needs to manually install iTwinUI, which might cause problems if the application and packages do not rely on the same iTwinUI version (see the next section for an example).- As a general rule, do not use peer deps if your package depends on an older version of
@itwin/itwinui-react
, because doing this will block applications from using the newest version of iTwinUI.
- As a general rule, do not use peer deps if your package depends on an older version of
- Avoid using internal APIs and CSS class names, as these can often break between versions.
iui-
and_iui
prefixed classes are considered an implementation detail and you should not target them; instead, use your own custom class names or data attributes. - In most cases, there is no need to explicitly install
@itwin/itwinui-css
or@itwin/itwinui-variables
. Just@itwin/itwinui-react
should be enough.- Starting with v2.12.0,
@itwin/itwinui-react
does not even depend on@itwin/itwinui-css
or@itwin/itwinui-variables
. - Even in prior versions of
@itwin/itwinui-react
, these dependencies should have been automatically installed, so there is no reason for them to be present in any of yourpackage.json
files unless you are directly using them elsewhere (uncommon). - If you are referencing CSS variables from iTwinUI (for example
var(--iui-color-background)
) in your code, you still do not need to install or import anything. All nodes under<ThemeProvider>
should have automatic access to these CSS variables.
- Starting with v2.12.0,
It may not be feasible for your app to upgrade entirely to iTwinUI v2 all at once, because some of your dependencies might still be on v1. To alleviate this, we have added the ability to incrementally migrate to v2 for parts of your app, while still using v1 for the rest of the page.
To use this feature, make sure all parts that use v1 are updated to @itwin/itwinui-react@1.48.3
. Then install the latest v2 patch of @itwin/itwinui-react
, and wrap your v2 parts in ThemeProvider
. Support for incremental migration was first added in @itwin/itwinui-react@2.5.0
and later improved in @itwin/itwinui-react@2.12.0
.
<body>
<!-- rest of your app (v1) -->
<ThemeProvider>
<!-- new UI built using v2 -->
</ThemeProvider>
</body>
You can see a basic demo of this which uses npm aliases.
In a real app, you might need to use (p)npm overrides to make sure that there is no internal dependency that accidentally uses a very old version of iTwinUI v1. You can verify the versions in your lockfile. A compatible setup for overrides may include these versions:
"@itwin/itwinui-css@0": "^0.63.3",
"@itwin/itwinui-react@1": "^1.48.3",
"@itwin/itwinui-react@2": "^2.12.26"
Wrapping a region in ThemeProvider
creates a "v2 boundary" which means everything under it needs to be on v2. Make sure that there are no nodes that use v1 inside this boundary. Sometimes a package may be using v1 internally, which means you cannot create a v2 boundary around it until they update to v2.
Practically speaking, leaf nodes (and packages near leaf nodes) will be the first ones to migrate to v2, so they should use ThemeProvider
. However, because ThemeProvider
defaults to light theme, it could conflict with the application's theme. To work around this, we have added a new theme='inherit'
option, which will use parent theme (if found) and fallback to light theme.
<ThemeProvider theme='inherit'>
<!-- entry point of package -->
</ThemeProvider>
For more details, see isolating styles.
One issue you might run into is with portaled elements (e.g. modals, toasts) not working correctly inside the v2 boundary. The general workaround is to make sure these elements get portaled into v2 root (rather than the v1 root or <body>
). For more details, see this issue.
@itwin/itwinui-css@0.63.3
and @itwin/itwinui-react@1.48.3
), as there will be no more changes made to them.
At the time of writing, @itwin/itwinui-css
is still pre-1.0 but @itwin/itwinui-react
is at 1.X. Here's what this means in practice:
- For
@itwin/itwinui-css
:- Minor version bumps may include breaking changes.
- Usually this means a change in some CSS class names or structure; we try to minimize any changes in Sass variables or CSS variables.
-
Caret ranges in package.json do NOT include minor versions. e.g.:
@itwin/itwinui-css@^0.44.0
will NOT allow version0.45.0
.
- Minor version bumps may include breaking changes.
- For
@itwin/itwinui-react
:- The component API is considered stable and no breaking changes are introduced in minor version bumps.
- Minor version bumps from
@itwin/itwinui-css
(which include breaking CSS changes) are handled in a way that preserve the React component API. - A particular minor version of
@itwin/itwinui-react
may only be compatible with the exact version of@itwin/itwinui-css
that it lists as a dependency.
If your application has multiple dependencies which use iTwinUI, then it can be confusing to keep track of them and you might even see CSS conflicts.
To alleviate this situation, we have a few recommendations for you:
If your package has a direct dependency on iTwinUI, then your package.json
should:
- List
@itwin/itwinui-react
as a direct dependency with a caret (^
) to allow future minor versions. - NOT list
@itwin/itwinui-css
as a dependency, even if you are using some variables from it.
- "@itwin/itwinui-css": "~0.44.1"
- "@itwin/itwinui-react": "~1.29.3"
+ "@itwin/itwinui-react": "^1.29.3"
You should design your package in a way that maximizes the use of the stable React API and minimize the use of unstable CSS classes. For example, if you are building a custom alert, then you should use our Alert
component, and extend it using a custom className
rather than directly using our CSS classes or mixins which might change in the future.
export const CustomAlert = () => {
return (
- <div className='iui-alert more-alert-styles'>...</div>
+ <Alert classname='more-alert-styles'>...</Alert>
);
};
You can still make limited use of Sass or CSS variables from @itwin/itwinui-css
when necessary, through the transitive dependency. In the future, we might move these variables into a separate package to offer more stability.
The most important thing to remember here is that you need to make sure your lockfile only has ONE version of @itwin/itwinui-css
and ONE of @itwin/itwinui-react
. If the packages built on top of iTwinUI follow the recommendations above, then this should be pretty straightforward as your application is in control of the exact version that's intalled.
If the packages do not make it easy to synchronize to a single version, then you can force it to the version you want using yarn resolutions for yarn v1, npm overrides for npm v8 and later, or npm-force-resolutions for npm v7 and below.
Note: You might need to remove node-modules
and reinstall it for resolutions to work.