Skip to content

Approaches

Learn which approach is recommended, depending on the situation, to customize Joy UI components.

There are 3 main approaches for customizing Joy UI components:

  1. Theming (global customization)
  2. One-off customization (per instance)
  3. Reusable component (custom component)

Theming

If you want every instance of a given component to be styled consistently, theming is the way to go. You can think of it as a mask that wraps them and adds custom styles. That way, you should be able to switch between themes without changing your app's logic.

Here are some examples that reproduce popular designs out there (only the light mode, though):

import { CssVarsProvider, extendTheme } from '@mui/joy/styles';
import Button from '@mui/joy/Button';

const githubTheme = extendTheme({
  colorSchemes: {
    light: {
      palette: {
        success: {
          solidBg: '#2DA44E',
          solidHoverBg: '#2C974B',
          solidActiveBg: '#298E46',
        },
        neutral: {
          outlinedBg: '#F6F8FA',
          outlinedHoverBg: '#F3F4F6',
          outlinedActiveBg: 'rgba(238, 239, 242, 1)',
          outlinedBorder: 'rgba(27, 31, 36, 0.15)',
        },
        focusVisible: 'rgba(3, 102, 214, 0.3)',
      },
    },
  },
  focus: {
    default: {
      outlineWidth: '3px',
    },
  },
  fontFamily: {
    body: 'SF Pro Text, var(--gh-fontFamily-fallback)',
  },
  components: {
    JoyButton: {
      styleOverrides: {
        root: ({ ownerState }) => ({
          borderRadius: '6px',
          boxShadow: '0 1px 0 0 rgba(27, 31, 35, 0.04)',
          transition: '80ms cubic-bezier(0.33, 1, 0.68, 1)',
          transitionProperty: 'color,background-color,box-shadow,border-color',
          ...(ownerState.size === 'md' && {
            fontWeight: 600,
            minHeight: '32px',
            fontSize: '14px',
            '--Button-paddingInline': '1rem',
          }),
          ...(ownerState.color === 'success' &&
            ownerState.variant === 'solid' && {
              '--gh-palette-focusVisible': 'rgba(46, 164, 79, 0.4)',
              border: '1px solid rgba(27, 31, 36, 0.15)',
              '&:active': {
                boxShadow: 'inset 0px 1px 0px rgba(20, 70, 32, 0.2)',
              },
            }),
          ...(ownerState.color === 'neutral' &&
            ownerState.variant === 'outlined' && {
              '&:active': {
                boxShadow: 'none',
              },
            }),
        }),
      },
    },
  },
});

function App() {
  return (
    <CssVarsProvider theme={githubTheme}>
      <Button>Solid</Button>
      ...other buttons
    </CssVarsProvider>
  );
};

Customizing theme tokens

Joy UI's theme consists of two categories of tokens: low-level and global variant. Check the theme tokens article for detailed information about both.

Every component rely on them to define their look and feel. Therefore, if you want to print your own design language into Joy UI components in a scallable way, you should customize them.

To do that, always use the extendTheme function as the customized tokens will be deeply merged into the default theme. Under the hood, Joy will convert the tokens to CSS variables, enabling you to get them through theme.vars.*, which is very convenient as you can use any styling solution to read those CSS vars.

import { CssVarsProvider, extendTheme } from '@mui/joy/styles';

const theme = extendTheme({
  colorSchemes: {
    light: {
      palette: {
        // affects all Joy components that has `color="primary"` prop.
        primary: {
          50: '#fffbeb',
          100: '#fef3c7',
          200: '#fde68a',
          // 300, 400, ..., 800,
          900: '#78350f',
        },
      },
    },
  },
  fontFamily: {
    display: 'Inter var, var(--joy-fontFamily-fallback)',
    body: 'Inter, var(--joy-fontFamily-fallback)',
  },
});

function App() {
  return <CssVarsProvider theme={theme}>...</CssVarsProvider>;
}

Themed components

Customizing the theme tokens, as described above, will affect every Joy UI component. However, if you want to customize a specific component and want every instance of it to be consistent, you can target it directly within the theme.

Here is an example of customizing the Button's default fontWeight and boxShadow:

import { CssVarsProvider, extendTheme } from '@mui/joy/styles';
import Button from '@mui/joy/Button';

const theme = extendTheme({
  components: {
    // The identifier always start with `Joy${ComponentName}`.
    JoyButton: {
      styleOverrides: ({ theme }) => ({
        // theme.vars.* return the CSS variables.
        fontWeight: theme.vars.fontWeight.lg, // 'var(--joy-fontWeight-lg)'
        boxShadow: theme.vars.shadow.xs, // 'var(--joy-shadow-xs)'
      }),
    },
  },
});

function App() {
  return (
    <CssVarsProvider theme={theme}>
      <Button>Text</Button>
    </CssVarsProvider>
  );
}

One-off customization

sx prop

To change the style of one single instance of a component, the sx prop is the recommended option. It can be used with all Joy UI components.

Reusable component

styled function

To create a custom and reusable component, the styled function is the recommended option. You can access the theme and use its design tokens, maintaining consistency. Additionally, the created component also accepts the sx prop if you ever want to do one-off customizations to it.