Skip to content

[material][styles] Add optimizedTheme type feature to reduce TS instantiations - #49004

Open
siriwatknp wants to merge 2 commits into
mui:masterfrom
siriwatknp:feat/theme-type-features
Open

[material][styles] Add optimizedTheme type feature to reduce TS instantiations#49004
siriwatknp wants to merge 2 commits into
mui:masterfrom
siriwatknp:feat/theme-type-features

Conversation

@siriwatknp

@siriwatknp siriwatknp commented Aug 21, 2026

Copy link
Copy Markdown
Member

closes #42772, closes #47099

Alternative to #47069 based on this analysis.

Summary

Type checking a custom theme forces TypeScript to instantiate the types of all components through theme.components. In large projects this slows down type checking and can cause out-of-memory errors in CI.

This PR adds an opt-in optimizedTheme type feature. Users enable it with one module augmentation, no import changes:

declare module '@mui/material/styles' {
  interface TypeFeatures {
    optimizedTheme: true;
  }
}

With the flag on, theme.components becomes a loose ThemeComponents interface. Type safety comes back per component through augmentation:

import { Components, Theme } from '@mui/material/styles';

declare module '@mui/material/styles' {
  interface TypeFeatures {
    optimizedTheme: true;
  }
  interface ThemeComponents extends Pick<Components<Theme>, 'MuiButton'> {}
}

Measured with the new test/ts-performance workspace against the built package (TypeScript 6.0.3):

scenario instantiations check time memory
flag off (today's behavior) 145,744 6.24s 183MB
flag on 2,079 0.06s 112MB
flag on + stray styles/root barrel imports no change no change no change

Why not a separate entry point (#47069)

The stylesOptimized entry point restores the strict types through a global module augmentation, so one import of @mui/material/styles or @mui/material anywhere in the program disables the optimization. Dependencies (MUI X, Toolpad, theme libraries) make this unavoidable for most real apps. Measured: one 2-line file importing createTheme({}) from styles takes the pristine program from 187 back to 145k instantiations.

The flag is immune to this: it is not tied to import paths, so stray imports and dependencies cannot turn it off. It also keeps a single Theme/ThemeOptions identity, so existing ecosystem augmentations of @mui/material/styles keep working, and no mirror entry point or codemod is needed.

For Reviewers

  • TypeFeatures and ThemeComponents are declared in createThemeNoVars.d.ts, following the same pattern as the existing CssThemeVariables toggle.
  • The four sites that referenced Components<...> (Theme/ThemeOptions, CssVarsThemeOptions, ThemeWithProps) now go through ResolvedComponents<T>, a conditional type that picks ThemeComponents when the flag is on. Conditional type branches are lazy, so the strict branch is never instantiated in optimized mode. With the flag off, resolution is identical to today (verified: same instantiation count).
  • The flag does not shrink the loaded file graph (components.ts still imports every component's props), so parse time remains. The instantiation explosion, which is what causes the OOM, is removed.
  • A published library shipping the flag augmentation would enable it for its consumers. The docs warn against this, same hazard class as themeCssVarsAugmentation.
  • test/ts-performance runs after release:build in CI (new Test theme type instantiations step). A shared fixture customizing every component is checked by two projects: the strict one asserts default strictness with @ts-expect-error and reports the >100k baseline; the optimized one enables the flag, imports from the root barrel, fails above 20k instantiations, and asserts the selective MuiButton augmentation still rejects invalid values so a silently dropped augmentation fails CI.

To test locally:

pnpm --filter "@mui/material..." build
pnpm --filter @mui-internal/test-ts-performance test:performance

@code-infra-dashboard

code-infra-dashboard Bot commented Aug 21, 2026

Copy link
Copy Markdown

Deploy preview

Bundle size

Bundle Parsed size Gzip size
@mui/material 0B(0.00%) 0B(0.00%)
@mui/lab 0B(0.00%) 0B(0.00%)
@mui/private-theming 0B(0.00%) 0B(0.00%)
@mui/system 0B(0.00%) 0B(0.00%)
@mui/utils 0B(0.00%) 0B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

@siriwatknp
siriwatknp marked this pull request as ready for review August 21, 2026 09:18
@zannager zannager added the scope: styles Specific to @mui/styles. Legacy package, @material-ui/styled-engine is taking over in v5. label Aug 21, 2026
@zannager
zannager requested a review from brijeshb42 August 21, 2026 13:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: styles Specific to @mui/styles. Legacy package, @material-ui/styled-engine is taking over in v5.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Passing a Theme object into createTheme increases compile time Some bad ts performance cases

2 participants