Enhance density
Apply consistent, adjustable sizing across Material UI components.
Starting from v9.5, Material UI provides an opt-in theme enhancer that maps most components onto one shared spacing scale. Same-size controls end up on the same box, and one scale controls the whole set.
Usage
Apply enhanceDensity to a theme and pass the result to ThemeProvider, components under the provider will start using the new scale:
import {
createTheme,
unstable_enhanceDensity as enhanceDensity,
ThemeProvider,
} from '@mui/material/styles';
const theme = enhanceDensity(createTheme());
function App() {
return <ThemeProvider theme={theme}>{/* ... */}</ThemeProvider>;
}
The demo below demonstrates the effect of enhanceDensity on a Button between sizes.
With the enhancer, the Button's CSS implementation switches from raw pixel values to the new spacing scale and the touchTarget size.
The enhancer also modernizes components like Button that use the margin-based spacing between children to use the gap property instead. This makes the component more resilient and easier to customize.
Benefits
Consistent sizing
The enhancer lets you set 2 sizing variables:
touchTarget(default 32px): the height of a medium interactive control, applied across the library so controls of the same size share the same box. By default, small controls are 28px and large controls are 44px.iconSize(default 16px): the size of a medium icon, applied toSvgIconand to the icons inside IconButton, Checkbox, Radio, Chip, and the Select caret. By default, small icons are 14px and large icons are 20px.
const theme = enhanceDensity(createTheme(), {
touchTarget: 40,
iconSize: 20,
});
iconSize
touchTarget
Spacing scale
Every component draws its spacing and sizing from the same fixed set of steps, so the parts that should line up across components share the same values:
| Step | Value | Typical use |
|---|---|---|
xxSmall |
4px | Icon-to-label gaps, tight insets |
xSmall |
8px | Compact padding, small control insets |
small |
12px | Default inline padding |
medium |
16px | Container padding |
large |
24px | Large inline padding, dialog margin |
xLarge |
32px | Wide insets |
xxLarge |
48px | Large surfaces |
Control heights do not come from these steps: they come from touchTarget, described above.
These are absolute values rather than multiples of the theme's spacing unit, so small is 12px whatever spacing is set to. To move them, pass a custom scale.
The scale rides the spacing API you already use. There's no new function to learn and no new theme node. theme.spacing() resolves step names alongside the numbers and raw CSS values it already accepts, and a leading dash negates a step:
const theme = enhanceDensity(createTheme());
theme.spacing('small'); // '12px'
theme.spacing('-xSmall'); // '-8px'
<Box sx={{ p: 'small', gap: 'xSmall' }} />;
// .Box-hashed-class { padding: 12px; gap: 8px; }
To use the scale in your theme component overrides, write a callback that receives the theme and calls theme.spacing(<scale>):
const customTheme = enhanceDensity(
createTheme({
components: {
MuiCard: {
styleOverrides: {
root: ({ theme }) => ({ padding: theme.spacing('medium') }),
},
},
},
}),
);
CSS variables support
On a theme created with CSS theme variables, the steps also ship as global CSS variables, and theme.spacing() returns a reference to them, with the step's px as a fallback:
const theme = enhanceDensity(createTheme({ cssVariables: true }));
theme.spacing('small'); // 'var(--mui-spacing-small, 12px)'
// A step given an explicit value falls back to that length:
const dense = enhanceDensity(createTheme({ cssVariables: true }), {
spacing: { small: 8 },
});
dense.spacing('small'); // 'var(--mui-spacing-small, 8px)'
The step variables are defined as plain px, not as multiples of --mui-spacing, so overriding the spacing unit from CSS moves theme.spacing(2) but leaves the named steps where they are.
This means the scale can be read, and overridden, from plain CSS, including for one region of the page:
.dense-region {
--mui-spacing-medium: 12px;
}
The two sizing constants also ship as variables:
.compact-region {
--mui-touchTarget: 28px;
--mui-iconSize: 14px;
}
Because they size a box rather than space one, they are not spacing keys. Read them off the theme instead:
const Control = styled('div')(({ theme }) => ({
height: (theme.vars || theme).touchTarget,
}));
On a CSS theme variables theme, theme.vars gives the variable reference, so the value still follows a CSS override; otherwise the theme carries the length itself.
TypeScript
The theme returned by enhanceDensity is typed with touchTarget and iconSize. To have them on the Theme type everywhere, for example in styled and sx callbacks, and to get the step names as theme.spacing() suggestions, import the density types once in your app:
import type {} from '@mui/material/themeDensityAugmentation';
These types are opt-in so that projects without the enhancer keep their own Theme augmentations and spacing types unchanged.
All components
enhanceDensity applies to every component listed in the demo below. Components outside it are left untouched. Select a component to see how its dimensions map to the spacing scale: the padding ring, the gap between children, and the height the control settles at, each measured off the rendered element and named back to the step that produced it.
Customizing the scale
To override the defaults, pass a second argument to enhanceDensity.
Below is an example of a full scale overrides for a very dense application:
const theme = enhanceDensity(createTheme(), {
spacing: {
xxSmall: 2,
xSmall: 4,
small: 8,
medium: 12,
large: 16,
xLarge: 24,
xxLarge: 32,
},
touchTarget: 24,
iconSize: 14,
});
The scale is a closed set of seven steps, plus the two sizings. The values must be numbers, which are interpreted as pixels. Anything left out keeps its default value.
The numbers the theme ended up with are readable as theme.unstable_densityScale (spacing, touchTarget, iconSize), for code that derives sizes in JavaScript rather than CSS.
Density recipes
This recipe demonstrates a design system that needs multiple densities to support various context of applications. The densities are defined as low/medium/high, each with its own set of spacing values and target sizes. Everything else stays on the default theme.
Use the density select at the top to see the differences between each density. To see the actual values of each density, click the "Show code" and find the ./densityRecipes.ts file.
Pricing
Start free. Move up a plan when you need more room.
Team
For growing product teams
$18
per editor, billed yearly
Includes:
Version history and restore
Shared workspaces
Unlimited project archive
Scheduled exports
Extra regions (add-on)
Organization
Volume pricing, audit logs, and onboarding support.