The sx Prop in Material UI: Fast, Theme-Aware Styling Without the Boilerplate
Material UI's sx prop puts responsive, theme-aware styling directly in JSX. Here's how the array syntax and theme tokens work, with a realistic look at the trade-offs.
20 Aug 2025, 04:32 UTC

You need a button with a bit more padding on desktop, a slightly larger font on tablets, and your brand color pulled from the theme. The traditional route means a new class name, a CSS file (or a styled component), media queries, and a mental mapping between the two files. For a one-off tweak, that's a lot of ceremony.
Material UI's sx prop collapses that ceremony into a single object written directly on the component. It's theme-aware, responsive out of the box, and available on every MUI component. This post explains how it works, shows a concrete responsive example, and covers where it stops being the right tool.
What the sx prop actually is
The sx prop accepts a CSS-in-JS object that MUI (via Emotion, its default styling engine) converts into a generated class at runtime. It looks like inline styles, but it isn't: you get pseudo-selectors, media queries, nesting, and — most importantly — direct access to your theme.
Two conveniences make it feel different from raw CSS-in-JS:
- Theme shorthands. Keys like
p(padding),m(margin), andbgcolormap to theme values.p: 2meanstheme.spacing(2)— 16px with the default 8px spacing unit — not 2px. - Theme tokens as values. Strings like
'primary.main'or'text.secondary'resolve against your palette, so colors stay consistent when you switch themes or add dark mode.
Responsive values without media queries
The feature that sells most people on sx is the array syntax. Pass an array and each index corresponds to a breakpoint, mobile-first: index 0 is the base style, index 1 applies from sm up, index 2 from md up, and so on. An object form ({ xs: ..., md: ... }) works too if you prefer named breakpoints.
A worked example: a responsive call-to-action button
Here's a button that grows padding and font size as the viewport widens, and pulls its color from the theme:
import Button from '@mui/material/Button';
export default function CtaButton() {
return (
<Button
variant="contained"
sx={{
p: [1, 1.5, 2], // 8px → 12px → 16px padding
fontSize: ['0.875rem', '1rem', '1.125rem'],
bgcolor: 'primary.main',
'&:hover': {
bgcolor: 'primary.dark',
},
}}
>
Get started
</Button>
);
}
Run this in any React app with @mui/material, @emotion/react, and @emotion/styled installed (MUI v5 or later — check with npm list @mui/material). Resize the browser window or toggle device emulation in dev tools and you'll see the padding and font size step up at the sm (600px) and md (900px) default breakpoints. The &:hover key shows that pseudo-selectors work the same way they do in Emotion's css prop.
Because bgcolor: 'primary.main' reads from the theme, wrapping this button in a custom ThemeProvider automatically restyles it — no code change needed.
Where sx falls short
The sx prop is not free, and it's worth knowing the costs before sprinkling it everywhere:
- Render overhead. The style object is recreated on every render and serialized by Emotion. For a handful of components this is negligible; for a list rendering hundreds of rows, hoist stable style objects to module-level constants (or memoize them) so Emotion can cache effectively.
- Readability. Fifteen lines of
sxon every element in a layout quickly buries the JSX structure. Once a style is reused or grows past a few declarations,styled()gives you a named component and a single source of truth. - Specificity surprises. Generated styles can have higher specificity than plain external stylesheets, which occasionally makes overriding third-party CSS (or letting third-party CSS override MUI) awkward. If you hit this, the
StyledEngineProviderwithinjectFirstis the usual fix.
A pragmatic split
A rule of thumb that holds up well in practice: use sx for component-level, contextual tweaks — spacing adjustments, responsive behavior, one-off alignment — and use styled() for anything reused across more than a couple of places or complex enough to deserve a name. The two compose cleanly, since styled() components also accept sx.
To try it today: pick one static style block in an existing MUI v5+ project, convert it to sx with array values for the breakpoints, and verify by resizing the browser with dev tools open. If the JSX stays readable and the responsive behavior checks out, you've found the prop's sweet spot.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.