@mui/joy, @mui/system, and @mui/styled-engine represent different layers of the Material-UI styling architecture. @mui/joy is a complete UI component library with its own modern design system and CSS variable-based theming. @mui/system provides low-level styling utilities, such as the sx prop, responsive values, and theme providers, allowing you to build custom components with MUI's design tokens. @mui/styled-engine is the underlying abstraction layer that powers the styled function, handling the actual CSS-in-JS injection, but it is rarely used directly by application developers compared to the higher-level system utilities.
When working within the Material-UI ecosystem, developers often encounter three distinct packages that handle styling: @mui/joy, @mui/system, and @mui/styled-engine. While they all relate to how CSS is applied to React components, they serve very different roles in your architecture. Understanding these layers prevents over-engineering and ensures you pick the right tool for your specific needs.
@mui/joy is a full-featured UI component library.
It provides ready-to-use components like Buttons, Sheets, and Inputs that follow a specific design language.
It includes its own theming engine based on CSS variables, making runtime theme switching easy.
// @mui/joy: Complete component with built-in styles
import { Button } from '@mui/joy';
function App() {
return <Button variant="solid" color="primary">Click Me</Button>;
}
@mui/system is a set of low-level styling utilities.
It does not provide UI components like Buttons. Instead, it gives you tools to build them.
It exports the Box component, the sx prop, and theme providers that work with any CSS-in-JS library.
// @mui/system: Utility for custom styling
import { Box } from '@mui/system';
function App() {
return (
<Box sx={{ p: 2, bgcolor: 'primary.main', borderRadius: 1 }}>
Custom Styled Box
</Box>
);
}
@mui/styled-engine is the underlying mechanism.
It handles the actual injection of styles into the DOM.
It is rarely imported directly; instead, it powers the styled function found in @mui/system or @mui/joy.
// @mui/styled-engine: Internal engine usage (Rare)
import styled from '@mui/styled-engine';
// This is what powers the styled() export in higher packages
const Div = styled('div')`
color: blue;
`;
@mui/joy relies heavily on CSS variables.
Themes are converted into CSS variables at runtime.
This allows for dynamic theme switching without re-rendering the entire component tree.
// @mui/joy: CSS Variable Theming
import { CssVarsProvider, extendTheme } from '@mui/joy/styles';
const theme = extendTheme({
colorScheme: 'dark',
});
function App() {
return <CssVarsProvider theme={theme}>{/* children */}</CssVarsProvider>;
}
@mui/system uses JavaScript theme objects.
It expects a theme structure (palette, spacing, typography) passed via ThemeProvider.
Values are resolved at render time, which can be slightly heavier than CSS variables but offers strong TypeScript support.
// @mui/system: JS Object Theming
import { ThemeProvider, createTheme } from '@mui/system';
const theme = createTheme({
palette: { primary: { main: '#1976d2' } },
});
function App() {
return <ThemeProvider theme={theme}>{/* children */}</ThemeProvider>;
}
@mui/styled-engine does not enforce a theme structure.
It simply injects CSS strings or objects.
Any theming logic must be manually implemented by the developer using it directly.
// @mui/styled-engine: Manual Theming
import styled from '@mui/styled-engine';
const StyledDiv = styled('div')(({ theme }) => ({
color: theme?.primary || 'black', // Manual theme access
}));
sx Prop and Style Props@mui/joy includes the sx prop by default.
Every component accepts sx for quick overrides using the Joy theme tokens.
It is optimized for the Joy design system's specific spacing and color scales.
// @mui/joy: sx prop on components
import { Card } from '@mui/joy';
function App() {
return <Card sx={{ boxShadow: 'lg', p: 3 }}>Content</Card>;
}
@mui/system is the source of the sx prop logic.
You can add the sx prop to any custom component using the styleFunctionSx utility.
This is useful when building a design system that isn't Joy or Material.
// @mui/system: Adding sx to custom components
import { styleFunctionSx, useTheme } from '@mui/system';
function MyComponent(props) {
const theme = useTheme();
// Apply sx styles manually or via HOC
return <div style={styleFunctionSx(props.sx, theme)}>{props.children}</div>;
}
@mui/styled-engine does not support the sx prop out of the box.
It focuses purely on the styled tagged template literal or object syntax.
You would need to build the sx parsing logic yourself on top of this engine.
// @mui/styled-engine: No built-in sx
import styled from '@mui/styled-engine';
// You must manually handle style props
const Div = styled('div')(({ myProp }) => ({
padding: myProp === 'large' ? '2rem' : '1rem',
}));
@mui/joy is the heaviest option.
It includes all components, icons, and the theming engine.
Use it when you need the full suite of UI elements and don't mind the bundle size.
@mui/system is lighter than Joy.
It strips away the UI components but keeps the logic for spacing, palette, and responsive styles.
Ideal for projects that already have UI components but want MUI's styling power.
@mui/styled-engine is the lightest but requires the most work.
It is a peer dependency for the other packages.
Using it alone means you lose the convenience utilities that make MUI popular.
| Feature | @mui/joy | @mui/system | @mui/styled-engine |
|---|---|---|---|
| Primary Role | UI Component Library | Styling Utilities | CSS-in-JS Engine |
| Components | β Buttons, Inputs, Layouts | β None (Box only) | β None |
| Theming | CSS Variables | JS Objects | Manual/None |
sx Prop | β Built-in | β Available | β Not Included |
| Best For | New Apps, Dashboards | Custom Design Systems | Library Authors |
@mui/joy is your go-to for building interfaces quickly.
It handles the design details so you can focus on application logic.
Choose this if you want a modern look with minimal configuration.
@mui/system is for when you need flexibility.
It lets you use MUI's powerful styling engine without locking you into Material or Joy components.
Choose this if you are building your own component library or migrating an existing one.
@mui/styled-engine is the foundation.
Most developers will never import it directly.
It exists to ensure @mui/system and @mui/joy work efficiently under the hood.
Final Thought: For most application developers, the choice is between @mui/joy (for complete UI) and @mui/system (for styling utilities). Treat @mui/styled-engine as an internal detail unless you are building a competing styling library.
Choose @mui/joy if you are starting a new project and want a complete, opinionated UI kit with modern aesthetics out of the box. It is ideal for teams that prefer CSS variables for theming and need accessible, pre-built components like buttons, inputs, and layouts without configuring a design system from scratch.
Avoid choosing @mui/styled-engine for direct use in application code, as it is designed as an internal engine for the other packages. It is only relevant if you are building a custom styling library that needs to replicate MUI's underlying CSS-in-JS mechanics, but for 99% of use cases, @mui/system or @mui/joy provides the stable public API you need.
Choose @mui/system if you need to add MUI's powerful style props (like margin, padding, display) to your own custom components or integrate MUI's theme provider into an existing project. It is the right choice when you want the utility of the sx prop and theme access without being tied to a specific set of UI components.