[system] Avoid iterating all styleOverrides keys per slot in createStyled
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 99.1k
- Forks
- 32.5k
- Avg merge
- 2d 17h
- Merged PRs (30d)
- 106
Description
Summary
createStyled iterates over all styleOverrides keys for every slot, then overridesResolver picks only the relevant ones. This is unnecessary work that compounds as components grow in slots and override keys. It also caused a class of runtime errors (ownerState undefined) that required per-component workarounds.
Current behavior
In createStyled.js, styleThemeOverrides does this:
for (const slotKey in styleOverrides) {
resolvedStyleOverrides[slotKey] = processStyle(
props,
styleOverrides[slotKey],
...
);
}
return overridesResolver(props, resolvedStyleOverrides);
Every slot's styled component resolves all styleOverrides entries, then overridesResolver cherry-picks the needed ones.
Compounding cost — OutlinedInput example
OutlinedInput has 3 styled slots (Root, NotchedOutline, Input) and up to 15 styleOverrides keys (root, colorSecondary, focused, disabled, adornedStart, adornedEnd, error, sizeSmall, multiline, notchedOutline, input, inputSizeSmall, inputMultiline, inputAdornedStart, inputAdornedEnd).
With the current implementation, each render triggers:
- Slot
Root→ iterates 15 keys, uses ~10 - Slot
NotchedOutline→ iterates 15 keys, uses 1 - Slot
Input→ iterates 15 keys, uses ~7
Total: 45 processStyle calls per render, ~27 of which are discarded. If styleOverrides values are callbacks, each discarded call still executes the function with the wrong slot's props.
For TextField (which composes InputBase + OutlinedInput + InputLabel + FormHelperText), the waste multiplies further.
Runtime errors from the same root cause
Because every slot calls every styleOverrides callback with its own props, slots that don't receive ownerState crash when callbacks depend on it:
- #31982 —
MuiOutlinedInputroot callback called byNotchedOutlineslot (no ownerState) - #43992 — Autocomplete styleOverrides crash on open
- #36223 — Avatar ownerState undefined
- #32799 — Alert ownerState undefined
- #38707 — Tabs ownerState undefined
These were fixed with per-component workarounds (passing ownerState to internal slots). The systemic fix was deferred because overridesResolver accesses multiple keys from the resolved object — changing it would be breaking.
Proposed behavior
Each slot should only resolve its own styleOverrides entry:
// Instead of iterating all keys:
const resolved = processStyle(props, styleOverrides[slot], ...);
return resolved;
This requires changing overridesResolver to receive the resolved style directly instead of a map of all resolved styles. Components like InputBase that currently read multiple keys would need to be updated.
Context
- Existing TODO:
// TODO: v7 remove iteration and use resolveStyleArg(styleOverrides[slot]) directly - PR #40690 added the TODO
- PR #33241 documented the breaking change blocker
- oliviertassinari's analysis: https://github.com/mui/material-ui/issues/31982#issuecomment-1093514995
Search keywords:
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in createStyled.js at styleThemeOverrides and follow overridesResolver, then inspect the existing TODO and InputBase's multiple-key access. Trace the affected styled slots and component workarounds; done means each slot resolves only its own styleOverrides entry without discarded callback calls, with all required resolver behavior updated.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, react
- Domain
- frontend, performance
- Issue type
- Refactor
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100