Customizing MUI Base components
There are several ways to customize MUI Base components, from applying custom CSS rules to building fully custom components using hooks.
With MUI Base, you have the freedom to decide how much you want to customize a component's structure and style.
This document reviews the three levels of customization that are available: applying custom CSS rules, overriding default subcomponent slots, and using hooks to build fully custom components.
Applying custom CSS rules
If you're happy with the default structure of a component's rendered HTML, you can apply custom styles to the component's classes.
Each component has its own set of classes. Some classes are static, which is to say that they are always present on the component. Others are applied conditionally—like Mui-disabled
, for example, which is only present when a component is disabled.
Each component's API documentation lists all classes that the component uses. Additionally, you can import a [componentName]Classes
object that describes all the classes a given component uses, as the following demo illustrates:
import * as React from 'react';
import SwitchUnstyled, { switchUnstyledClasses } from '@mui/base/SwitchUnstyled';
const css = `
.my-switch {
font-size: 0;
position: relative;
display: inline-block;
width: 32px;
height: 20px;
background: #B3C3D3;
border-radius: 10px;
margin: 10px;
cursor: pointer;
}
.my-switch.${switchUnstyledClasses.checked} {
background: #007FFF;
}
.my-switch .${switchUnstyledClasses.thumb} {
display: block;
width: 14px;
height: 14px;
top: 3px;
left: 3px;
border-radius: 16px;
background-color: #FFF;
position: relative;
transition: all 200ms ease;
}
.my-switch.${switchUnstyledClasses.checked} .${switchUnstyledClasses.thumb} {
left: 14px;
top: 3px;
background-color: #FFF;
}
.my-switch .${switchUnstyledClasses.input} {
cursor: inherit;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
opacity: 0;
z-index: 1;
margin: 0;
}`;
export default function StylingCustomCss() {
return (
<div>
<style type="text/css">{css}</style>
<SwitchUnstyled className="my-switch" />
</div>
);
}
Overriding subcomponent slots
If you want to make changes to a component's rendered HTML structure, you can override the default subcomponents ("slots") using the components
and/or component
prop—see "Shared props" on the Base Usage page for more details.
The following demo uses SwitchUnstyled to show how to create a styled component by applying styles to three of its subcomponent slots: Root
, Thumb
, and Input
.
Note that although this demo uses MUI System as a styling solution, you are free to choose any alternative.
<SwitchUnstyled slots={{ root: Root, thumb: Thumb, input: Input }} />
The components you pass in the components
prop receive the ownerState
prop from the top-level component (the "owner"). By convention, it contains all props passed to the owner, merged with its rendering state.
For example:
<SwitchUnstyled components={{ Thumb: MyCustomThumb }} data-foo="42" />
In this case, MyCustomThumb
component receives the ownerState
object with the following data:
{
checked: boolean;
disabled: boolean;
focusVisible: boolean;
readOnly: boolean;
'data-foo': string;
}
You can use this object to style your component.
Creating custom components using hooks
If you need complete control over a component's rendered HTML structure, you can build it with hooks.
Hooks give you access to the logic that a component uses, but without any default structure. See "Components vs. hooks" on the Base Usage page for more details.
Hooks return the current state of the component (e.g. checked
, disabled
, open
, etc.) and provide functions that return props you can apply to your fully custom components.
In the case of SwitchUnstyled, the component is accompanied by the useSwitch
hook which gives you all of the functionality without any structure.
It returns the following object:
{
checked: Readonly<boolean>;
disabled: Readonly<boolean>;
readOnly: Readonly<boolean>;
focusVisible: Readonly<boolean>;
getInputProps: (otherProps?: object) => SwitchInputProps;
}
The checked
, disabled
, readOnly
, and focusVisible
fields represent the state of the switch. Use them to apply styling to your HTML elements.
The getInputProps
function can be used to get the props to place on an HTML <input>
to make the switch accessible.
import * as React from 'react';
import clsx from 'clsx';
import { useSwitch, UseSwitchParameters } from '@mui/base/SwitchUnstyled';
import { styled } from '@mui/system';
const BasicSwitchRoot = styled('span')`
font-size: 0;
position: relative;
display: inline-block;
width: 32px;
height: 20px;
background: #b3c3d3;
border-radius: 10px;
margin: 10px;
cursor: pointer;
&.Switch-disabled {
opacity: 0.4;
cursor: not-allowed;
}
&.Switch-checked {
background: #007fff;
}
`;
const BasicSwitchInput = styled('input')`
cursor: inherit;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
opacity: 0;
z-index: 1;
margin: 0;
`;
const BasicSwitchThumb = styled('span')`
display: block;
width: 14px;
height: 14px;
top: 3px;
left: 3px;
border-radius: 16px;
background-color: #fff;
position: relative;
transition: all 200ms ease;
&.Switch-focusVisible {
background-color: rgba(255, 255, 255, 1);
box-shadow: 0 0 1px 8px rgba(0, 0, 0, 0.25);
}
&.Switch-checked {
left: 14px;
top: 3px;
background-color: #fff;
}
`;
function BasicSwitch(props: UseSwitchParameters) {
const { getInputProps, checked, disabled, focusVisible } = useSwitch(props);
const stateClasses = {
'Switch-checked': checked,
'Switch-disabled': disabled,
'Switch-focusVisible': focusVisible,
};
return (
<BasicSwitchRoot className={clsx(stateClasses)}>
<BasicSwitchThumb className={clsx(stateClasses)} />
<BasicSwitchInput {...getInputProps()} aria-label="Demo switch" />
</BasicSwitchRoot>
);
}
export default function StylingHooks() {
return <BasicSwitch />;
}