--- name: joy-ui description: Joy UI — MUI's alternative design system with CSS variables, modern aesthetics, and simpler API triggers: - Joy UI - joy - "@mui/joy" - alternative design allowed-tools: - Read - Glob - Grep - Write - Edit globs: - "*.tsx" - "*.ts" --- # Joy UI ## What is Joy UI? Joy UI (`@mui/joy`) is MUI's alternative component library that offers a modern, clean design language distinct from Google's Material Design. It is built from the ground up with CSS variables as a first-class feature, a flexible variant system, and a simpler API surface compared to Material UI. ```bash npm install @mui/joy @emotion/react @emotion/styled # or pnpm add @mui/joy @emotion/react @emotion/styled ``` Joy UI shares MUI's engineering quality (accessibility, TypeScript, composability) but makes different design decisions: - **CSS variables by default** -- not experimental, not opt-in - **Variant system** -- every component supports `solid`, `soft`, `outlined`, `plain` - **Color system** -- semantic colors (`primary`, `neutral`, `danger`, `success`, `warning`) instead of `primary`/`secondary` - **Decoration levels** -- replaces Material UI's `elevation` with a more flexible concept - **Modern aesthetic** -- rounded, spacious, contemporary look out of the box ## When to Use Joy UI | Scenario | Recommendation | |----------|---------------| | App should NOT look like Material Design | Joy UI | | Want CSS variables without experimental flags | Joy UI | | Need Google Material Design compliance | Material UI | | Building a modern SaaS dashboard | Joy UI | | Existing Material UI codebase | Material UI (or gradual migration) | | Need the largest component catalog | Material UI (more components today) | | Want built-in dark mode with zero config | Joy UI | ## Key Differences from Material UI ### Variant System Material UI uses `variant` on some components (`contained`, `outlined`, `text` for Button). Joy UI applies a consistent variant system across ALL components: | Variant | Description | Use Case | |---------|-------------|----------| | `solid` | Filled background, high emphasis | Primary actions, selected states | | `soft` | Subtle background tint | Secondary actions, tags, badges | | `outlined` | Border only | Tertiary actions, form fields | | `plain` | No background or border | Low-emphasis, text-like actions | ```tsx import Button from '@mui/joy/Button'; import Chip from '@mui/joy/Chip'; import Alert from '@mui/joy/Alert'; // Every component supports all four variants Active Check your input ``` ### Color System Material UI: `primary`, `secondary`, `error`, `warning`, `info`, `success` Joy UI: `primary`, `neutral`, `danger`, `success`, `warning` ```tsx import Button from '@mui/joy/Button'; import Typography from '@mui/joy/Typography'; Error message ``` ### Component Naming Differences | Material UI | Joy UI | Notes | |-------------|--------|-------| | `Paper` | `Sheet` | Surface container | | `TextField` | `Input` / `Textarea` | Separate components, not a wrapper | | `AppBar` | `Header` (custom) | Joy UI does not ship AppBar; use `Sheet` | | `Snackbar` | `Snackbar` | Same name, different API | | `Fab` | No equivalent | Use `IconButton` with `variant="solid"` | | `Rating` | No equivalent | Not yet available in Joy UI | | `SpeedDial` | No equivalent | Not yet available | ### Decoration Levels vs Elevation Material UI uses `elevation={0..24}` for box-shadow depth. Joy UI uses CSS variables and the `shadow` prop: ```tsx import Sheet from '@mui/joy/Sheet'; // Joy UI -- shadow levels No shadow Small shadow Medium shadow Large shadow ``` ## Setup ### Basic Setup with CssVarsProvider ```tsx // src/main.tsx import { CssVarsProvider } from '@mui/joy/styles'; import CssBaseline from '@mui/joy/CssBaseline'; import App from './App'; function Root() { return ( ); } ``` `CssVarsProvider` replaces Material UI's `ThemeProvider`. It automatically: - Injects CSS variables into `:root` - Supports light/dark mode toggle without theme re-creation - Enables runtime theme switching without JavaScript recalculation ### Dark Mode Toggle ```tsx import { useColorScheme } from '@mui/joy/styles'; import IconButton from '@mui/joy/IconButton'; function ColorSchemeToggle() { const { mode, setMode } = useColorScheme(); return ( setMode(mode === 'dark' ? 'light' : 'dark')} > {mode === 'dark' ? : } ); } ``` No need to create separate light/dark themes. The CSS variables automatically switch. ## Theme Customization ### Using extendTheme ```tsx import { CssVarsProvider, extendTheme } from '@mui/joy/styles'; const customTheme = extendTheme({ colorSchemes: { light: { palette: { primary: { 50: '#eff6ff', 100: '#dbeafe', 200: '#bfdbfe', 300: '#93c5fd', 400: '#60a5fa', 500: '#3b82f6', 600: '#2563eb', 700: '#1d4ed8', 800: '#1e40af', 900: '#1e3a8a', }, success: { solidBg: '#16a34a', solidHoverBg: '#15803d', }, }, }, dark: { palette: { primary: { solidBg: '#3b82f6', solidHoverBg: '#2563eb', }, }, }, }, fontFamily: { body: '"Inter", var(--joy-fontFamily-fallback)', display: '"Inter", var(--joy-fontFamily-fallback)', }, typography: { h1: { fontSize: '2.25rem', fontWeight: 700, lineHeight: 1.2, }, h2: { fontSize: '1.875rem', fontWeight: 600, lineHeight: 1.3, }, }, radius: { sm: '6px', md: '8px', lg: '12px', }, components: { JoyButton: { styleOverrides: { root: { borderRadius: 'var(--joy-radius-md)', fontWeight: 600, }, }, defaultProps: { variant: 'solid', color: 'primary', }, }, JoyInput: { styleOverrides: { root: { '--Input-radius': 'var(--joy-radius-md)', }, }, }, }, }); function App() { return ( {/* app content */} ); } ``` ### Accessing CSS Variables in Custom Components ```tsx import { styled } from '@mui/joy/styles'; const CustomCard = styled('div')(({ theme }) => ({ padding: theme.spacing(2), borderRadius: theme.vars.radius.md, backgroundColor: theme.vars.palette.background.surface, border: `1px solid ${theme.vars.palette.divider}`, boxShadow: theme.vars.shadow.sm, // CSS variables are also accessible as plain CSS // background: 'var(--joy-palette-background-surface)', })); ``` ### Custom Tokens ```tsx const theme = extendTheme({ colorSchemes: { light: { palette: { // Custom semantic tokens brand: { 50: '#fdf2f8', 500: '#ec4899', 700: '#be185d', }, }, }, }, }); // TypeScript: augment the palette interface declare module '@mui/joy/styles' { interface PaletteRange { // already exists in Joy } interface Palette { brand: PaletteRange; } } ``` ## Component Examples ### Button ```tsx import Button from '@mui/joy/Button'; import IconButton from '@mui/joy/IconButton'; import ButtonGroup from '@mui/joy/ButtonGroup'; // Variants and colors // Sizes // With icons // Loading state // Icon button // Button group ``` ### Input and Textarea ```tsx import Input from '@mui/joy/Input'; import Textarea from '@mui/joy/Textarea'; import FormControl from '@mui/joy/FormControl'; import FormLabel from '@mui/joy/FormLabel'; import FormHelperText from '@mui/joy/FormHelperText'; // Basic input // With decorators (adornments) } endDecorator={} placeholder="Search..." /> // Form control with label and helper text Email Please enter a valid email address. // Textarea with auto-resize