diff --git a/packages/app-legacy/src/index.tsx b/packages/app-legacy/src/index.tsx index 05dcc024bf..d7699e3d9d 100644 --- a/packages/app-legacy/src/index.tsx +++ b/packages/app-legacy/src/index.tsx @@ -14,6 +14,7 @@ * limitations under the License. */ +import '@backstage/theme/MuiClassNameSetup'; import '@backstage/cli/asset-types'; import ReactDOM from 'react-dom/client'; import App from './App'; diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx index fd86261385..873670de0f 100644 --- a/packages/app/src/index.tsx +++ b/packages/app/src/index.tsx @@ -14,6 +14,7 @@ * limitations under the License. */ +import '@backstage/theme/MuiClassNameSetup'; import '@backstage/cli/asset-types'; import ReactDOM from 'react-dom/client'; import app from './App'; diff --git a/packages/create-app/templates/default-app/packages/app/src/index.tsx b/packages/create-app/templates/default-app/packages/app/src/index.tsx index 46f31902f4..3cc56c886c 100644 --- a/packages/create-app/templates/default-app/packages/app/src/index.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/index.tsx @@ -1,3 +1,4 @@ +import '@backstage/theme/MuiClassNameSetup'; import '@backstage/cli/asset-types'; import ReactDOM from 'react-dom/client'; import App from './App'; diff --git a/packages/create-app/templates/next-app/packages/app/src/index.tsx b/packages/create-app/templates/next-app/packages/app/src/index.tsx index ac9e52bdc1..6936ff50bc 100644 --- a/packages/create-app/templates/next-app/packages/app/src/index.tsx +++ b/packages/create-app/templates/next-app/packages/app/src/index.tsx @@ -1,3 +1,4 @@ +import '@backstage/theme/MuiClassNameSetup'; import '@backstage/cli/asset-types'; import ReactDOM from 'react-dom/client'; import App from './App'; diff --git a/packages/theme/package.json b/packages/theme/package.json index e95ae20b36..f27fc9e385 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -20,7 +20,14 @@ "directory": "packages/theme" }, "license": "Apache-2.0", - "sideEffects": false, + "sideEffects": [ + "./src/unified/MuiClassNameSetup.ts" + ], + "exports": { + ".": "./src/index.ts", + "./MuiClassNameSetup": "./src/unified/MuiClassNameSetup.ts", + "./package.json": "./package.json" + }, "main": "src/index.ts", "types": "src/index.ts", "files": [ diff --git a/packages/theme/src/unified/MuiClassNameSetup.ts b/packages/theme/src/unified/MuiClassNameSetup.ts new file mode 100644 index 0000000000..1c1b854211 --- /dev/null +++ b/packages/theme/src/unified/MuiClassNameSetup.ts @@ -0,0 +1,40 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { unstable_ClassNameGenerator as ClassNameGenerator } from '@mui/material/className'; + +/** + * This API is introduced in @mui/material (v5.0.5) as a replacement of deprecated createGenerateClassName & only affects v5 Material UI components from `@mui/*`. + * + * This needs to be configured before any MUI 5 component can possibly be imported. See: https://v5.mui.com/material-ui/experimental-api/classname-generator/#caveat + * + * ```packages/app/index.ts + * + * import '@backstage/theme/MuiClassNameSetup'; // must be the very first import! + * import '@backstage/cli/asset-types'; + * import ReactDOM from 'react-dom/client'; + * import app from './App'; + * import '@backstage/ui/css/styles.css'; + * + * ReactDOM.createRoot(document.getElementById('root')!).render(app); + * ``` + */ +ClassNameGenerator.configure(componentName => { + return componentName.startsWith('v5-') + ? componentName + : `v5-${componentName}`; +}); + +export {}; diff --git a/packages/theme/src/unified/UnifiedThemeProvider.tsx b/packages/theme/src/unified/UnifiedThemeProvider.tsx index 69d843ed3e..b93746dad4 100644 --- a/packages/theme/src/unified/UnifiedThemeProvider.tsx +++ b/packages/theme/src/unified/UnifiedThemeProvider.tsx @@ -27,7 +27,7 @@ import { Theme as Mui5Theme, } from '@mui/material/styles'; import { UnifiedTheme } from './types'; -import { unstable_ClassNameGenerator as ClassNameGenerator } from '@mui/material/className'; +import './MuiClassNameSetup'; /** * Props for {@link UnifiedThemeProvider}. @@ -41,15 +41,6 @@ export interface UnifiedThemeProviderProps { themeName?: string; } -/** - * This API is introduced in @mui/material (v5.0.5) as a replacement of deprecated createGenerateClassName & only affects v5 Material UI components from `@mui/*`. - * - * This call needs to be in the same module as the `UnifiedThemeProvider` to ensure that it doesn't get removed by tree shaking - */ -ClassNameGenerator.configure(componentName => { - return `v5-${componentName}`; -}); - // Background at https://mui.com/x/migration/migration-data-grid-v4/#using-mui-core-v4-with-v5 // Rather than disabling globals and custom seed, we instead only set a production prefix that // won't collide with Material UI 5 styles. We've already got the separate class name generator diff --git a/packages/theme/src/unified/index.ts b/packages/theme/src/unified/index.ts index 747662e195..2428902981 100644 --- a/packages/theme/src/unified/index.ts +++ b/packages/theme/src/unified/index.ts @@ -21,3 +21,4 @@ export { themes } from './themes'; export { UnifiedThemeProvider } from './UnifiedThemeProvider'; export type { UnifiedThemeProviderProps } from './UnifiedThemeProvider'; export type { UnifiedTheme, SupportedThemes, SupportedVersions } from './types'; +export * from './MuiClassNameSetup';