--- name: sx-styled description: MUI sx prop and styled() API for component styling triggers: - sx prop - styled - styling - CSS-in-JS - emotion - responsive styles allowed-tools: - Read - Glob - Grep - Write - Edit globs: - "*.tsx" - "*.ts" - "*.jsx" --- # MUI sx Prop and styled() API ## Overview MUI provides two primary APIs for styling components: the `sx` prop (inline, one-off styles with full theme access) and the `styled()` function (reusable styled components built on Emotion). Choose based on reuse: `sx` for single-use overrides, `styled()` for components used more than once. --- ## sx Prop The `sx` prop accepts a superset of CSS where values can reference theme tokens, respond to breakpoints, and use shorthand aliases. It is available on every MUI component and on the `Box` primitive. ### Basic usage ```tsx import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; // Plain CSS properties — camelCase content // Theme token references // Typography variants Heading text ``` ### System shorthands MUI maps single-letter aliases to CSS properties. These only work inside `sx` (and `styled` with the `system` utilities), not in plain Emotion. ```tsx ``` ### Theme-aware callback Use the callback form `(theme) => ({...})` when you need theme values that cannot be expressed as token strings, such as palette computed colors or custom spacing math. ```tsx ({ backgroundColor: theme.palette.mode === 'dark' ? theme.palette.grey[900] : theme.palette.grey[100], padding: theme.spacing(2, 3), // shorthand: vertical, horizontal border: `1px solid ${theme.palette.divider}`, borderRadius: theme.shape.borderRadius, transition: theme.transitions.create(['background-color'], { duration: theme.transitions.duration.short, }), })} /> ``` ### Responsive values — object syntax Pass an object keyed by breakpoint names. Values are applied from the named breakpoint upward (mobile-first). ```tsx ``` ### Responsive values — array syntax Arrays map values to breakpoints in order `[xs, sm, md, lg, xl]`. Use `null` to skip a breakpoint without changing the value. ```tsx ``` ### Pseudo-selectors and nested selectors The `sx` prop supports any CSS selector string as a key, enabling hover states, focus-visible, and targeting MUI's internal slot class names. ```tsx ``` --- ## styled() Function `styled()` is the Emotion `styled` function extended with MUI's theme and system shorthands. Use it to create reusable, named components. ### Basic styled component ```tsx import { styled } from '@mui/material/styles'; import Box from '@mui/material/Box'; const HeroSection = styled(Box)(({ theme }) => ({ display: 'flex', flexDirection: 'column', alignItems: 'center', padding: theme.spacing(8, 2), backgroundColor: theme.palette.background.default, [theme.breakpoints.up('md')]: { flexDirection: 'row', padding: theme.spacing(12, 4), }, })); // Usage

Welcome

``` ### Styled with props Accept custom props to drive conditional styles. Use TypeScript generics to type them. ```tsx interface CardContainerProps { variant?: 'elevated' | 'outlined' | 'filled'; selected?: boolean; } const CardContainer = styled(Box, { // Prevent non-HTML props from being forwarded to the DOM element shouldForwardProp: (prop) => prop !== 'variant' && prop !== 'selected', })(({ theme, variant = 'elevated', selected }) => ({ borderRadius: theme.shape.borderRadius * 2, padding: theme.spacing(2), cursor: 'pointer', transition: theme.transitions.create(['box-shadow', 'border-color'], { duration: theme.transitions.duration.short, }), ...(variant === 'elevated' && { boxShadow: selected ? theme.shadows[8] : theme.shadows[1], '&:hover': { boxShadow: theme.shadows[4] }, }), ...(variant === 'outlined' && { border: `1px solid`, borderColor: selected ? theme.palette.primary.main : theme.palette.divider, boxShadow: 'none', '&:hover': { borderColor: theme.palette.primary.light }, }), ...(variant === 'filled' && { backgroundColor: selected ? theme.palette.primary.light : theme.palette.action.hover, boxShadow: 'none', }), })); // Usage {children} ``` ### shouldForwardProp Always declare `shouldForwardProp` for custom boolean or string props to prevent React warnings about unknown DOM attributes. ```tsx import { styled } from '@mui/material/styles'; import Button from '@mui/material/Button'; const GradientButton = styled(Button, { shouldForwardProp: (prop) => prop !== 'gradient', })<{ gradient?: boolean }>(({ theme, gradient }) => ({ ...(gradient && { background: `linear-gradient(45deg, ${theme.palette.primary.main} 30%, ${theme.palette.secondary.main} 90%)`, color: theme.palette.common.white, '&:hover': { background: `linear-gradient(45deg, ${theme.palette.primary.dark} 30%, ${theme.palette.secondary.dark} 90%)`, }, }), })); ``` ### Extending an existing styled component ```tsx const PrimaryCard = styled(CardContainer)({ borderTop: '4px solid', borderTopColor: 'primary.main', }); ``` --- ## sx vs styled() vs Theme Overrides — Decision Guide | Scenario | Recommendation | |---|---| | One-off style on a single instance | `sx` prop | | Same styles used on 2+ instances | `styled()` | | Styles driven by custom props | `styled()` with `shouldForwardProp` | | Overriding a MUI component globally | Theme `components.MuiXxx.styleOverrides` | | Dynamic styles based on component state | `sx` callback or `styled()` with props | | Performance-sensitive render-heavy list | `styled()` (styles computed once) | | Quick prototype / layout tweak | `sx` prop | --- ## Performance Considerations The `sx` prop generates a new class name on every render when its value object changes identity. For components that render frequently (virtualized lists, animated items), prefer `styled()` or memoize the sx object. ```tsx // Bad — new object reference every render function ListItem({ item }) { return ( {item.label} ); } // Better — stable reference for static parts, sx only for dynamic const ItemBase = styled(Box)(({ theme }) => ({ padding: theme.spacing(2), })); function ListItem({ item }) { return ( {item.label} ); } // Alternative — useMemo for complex dynamic sx function ListItem({ item, index }) { const sxStyles = React.useMemo(() => ({ padding: 2, color: item.active ? 'primary.main' : 'text.primary', animationDelay: `${index * 50}ms`, }), [item.active, index]); return {item.label}; } ``` --- ## Common Patterns ### Dark/light mode conditional ```tsx theme.palette.mode === 'dark' ? 'grey.900' : 'grey.50', color: 'text.primary', }} /> ``` ### Combining sx arrays (MUI v5+) Pass an array of sx values to compose styles. Falsy entries are skipped. ```tsx ``` ### Full-bleed section within a Container ```tsx const FullBleed = styled(Box)(({ theme }) => ({ width: '100vw', position: 'relative', left: '50%', right: '50%', marginLeft: '-50vw', marginRight: '-50vw', backgroundColor: theme.palette.primary.main, padding: theme.spacing(4, 0), })); ```