Checkbox
Checkbox gives users a binary choice between multiple options in a series.
Show code 'use client' ;
import * as React from 'react' ;
import { Checkbox } from '@base_ui/react/Checkbox' ;
import { useTheme } from '@mui/system' ;
export default function UnstyledCheckboxIntroduction () {
return (
< div style = {{ display: 'flex' , gap: 12 }}>
< Checkbox.Root
className = "Checkbox"
aria-label = "Basic checkbox, on by default"
defaultChecked
>
< Checkbox.Indicator className = "Checkbox-indicator" >
< CheckIcon className = "Checkbox-icon" />
</ Checkbox.Indicator >
</ Checkbox.Root >
< Checkbox.Root
className = "Checkbox"
aria-label = "Basic checkbox, off by default"
>
< Checkbox.Indicator className = "Checkbox-indicator" >
< CheckIcon className = "Checkbox-icon" />
</ Checkbox.Indicator >
</ Checkbox.Root >
< Checkbox.Root
className = "Checkbox"
aria-label = "Disabled checkbox, on by default"
defaultChecked
disabled
>
< Checkbox.Indicator className = "Checkbox-indicator" >
< CheckIcon className = "Checkbox-icon" />
</ Checkbox.Indicator >
</ Checkbox.Root >
< Checkbox.Root
className = "Checkbox"
aria-label = "Disabled checkbox, off by default"
disabled
>
< Checkbox.Indicator className = "Checkbox-indicator" >
< CheckIcon className = "Checkbox-icon" />
</ Checkbox.Indicator >
</ Checkbox.Root >
< Styles />
</ div >
);
}
const grey = {
100 : '#E5EAF2' ,
300 : '#C7D0DD' ,
500 : '#9DA8B7' ,
600 : '#6B7A90' ,
800 : '#303740' ,
900 : '#1C2025' ,
};
function useIsDarkMode () {
const theme = useTheme ();
return theme . palette . mode === 'dark' ;
}
function Styles () {
// Replace this with your app logic for determining dark mode
const isDarkMode = useIsDarkMode ();
return (
< style >
{ `
.Checkbox {
all: unset;
box-sizing: border-box;
text-align: center;
width: 24px;
height: 24px;
padding: 0;
border-radius: 4px;
border: 2px solid ${ grey [ 600 ] };
background: none;
transition-property: background, border-color;
transition-duration: 0.15s;
}
.Checkbox[data-disabled] {
opacity: 0.4;
cursor: not-allowed;
}
.Checkbox:focus-visible {
outline: 2px solid ${ isDarkMode ? grey [ 600 ] : grey [ 500 ] };
outline-offset: 2px;
}
.Checkbox[data-checked] {
border-color: ${ grey [ 800 ] };
background: ${ grey [ 800 ] };
}
.Checkbox-indicator {
height: 100%;
display: inline-block;
visibility: hidden;
color: ${ isDarkMode ? grey [ 900 ] : grey [ 100 ] };
}
.Checkbox-indicator[data-checked] {
visibility: visible;
}
.Checkbox-icon {
width: 100%;
height: 100%;
}
@media (prefers-color-scheme: dark) {
.Checkbox {
border-color: ${ grey [ 500 ] };
}
.Checkbox[data-checked] {
border-color: ${ grey [ 300 ] };
background: ${ grey [ 300 ] };
}
.Checkbox:hover:not([data-disabled]) {
border-color: ${ grey [ 100 ] };
}
.Checkbox-indicator {
color: ${ grey [ 900 ] };
}
}
` }
</ style >
);
}
function CheckIcon ( props : React . SVGProps < SVGSVGElement >) {
return (
< svg
xmlns = "http://www.w3.org/2000/svg"
{ ... props }
width = "24"
height = "24"
viewBox = "0 0 24 24"
fill = "none"
>
< path
d = "M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"
fill = "currentColor"
/>
</ svg >
);
}
Base UI components are all available as a single package.
npm pnpm Yarn
npm install @base_ui/react
Once you have the package installed, import the component.
import { Checkbox } from '@base_ui/react/Checkbox';
Checkbox is composed of two components:
<Checkbox.Root />
renders a <button>
.
<Checkbox.Indicator />
renders a <span>
for providing a visual indicator. You could place an icon inside this component.
< Checkbox.Root >
< Checkbox.Indicator />
</ Checkbox.Root >
To make the Checkbox indeterminate, add the indeterminate
prop to override the appearance of the Checkbox. The Checkbox remains in an indeterminate state regardless of user interaction until set back to false
.
Show code 'use client' ;
import * as React from 'react' ;
import { styled } from '@mui/system' ;
import { Checkbox as BaseCheckbox } from '@base_ui/react/Checkbox' ;
export default function UnstyledCheckboxIndeterminate () {
return (
< div style = {{ display: 'flex' , gap: 12 }}>
< Checkbox aria-label = "Indeterminate checkbox" indeterminate >
< Indicator >
< HorizontalRuleIcon />
</ Indicator >
</ Checkbox >
< Checkbox aria-label = "Indeterminate disabled checkbox" indeterminate disabled >
< Indicator >
< HorizontalRuleIcon />
</ Indicator >
</ Checkbox >
</ div >
);
}
const blue = {
400 : '#3399FF' ,
600 : '#0072E6' ,
800 : '#004C99' ,
};
const grey = {
100 : '#E5EAF2' ,
};
const Checkbox = styled ( BaseCheckbox . Root )(
({ theme }) => `
width: 24px;
height: 24px;
padding: 0;
border-radius: 4px;
border: 2px solid ${ blue [ 600 ] };
background: none;
transition-property: background, border-color;
transition-duration: 0.15s;
outline: none;
&[data-disabled] {
opacity: 0.4;
cursor: not-allowed;
}
&:focus-visible {
outline: 2px solid ${ theme . palette . mode === 'dark' ? blue [ 800 ] : blue [ 400 ] };
outline-offset: 2px;
}
&[data-checked],
&[data-indeterminate] {
border-color: transparent;
background: ${ blue [ 600 ] };
}
` ,
);
const HorizontalRuleIcon = styled ( function HorizontalRuleIcon (
props : React . SVGProps < SVGSVGElement >,
) {
return (
< svg
xmlns = "http://www.w3.org/2000/svg"
{ ... props }
width = "24"
height = "24"
viewBox = "0 0 24 24"
fill = "none"
>
< path d = "M4 11h16v2H4z" fill = "currentColor" />
</ svg >
);
}) `
height: 100%;
width: 100%;
` ;
const Indicator = styled ( BaseCheckbox . Indicator ) `
color: ${ grey [ 100 ] };
height: 100%;
display: inline-block;
visibility: hidden;
&[data-checked],
&[data-indeterminate] {
visibility: visible;
}
` ;
The primary use case for an indeterminate checkbox is representing the state of a parent checkbox where only some of its children are checked.
Show code 'use client' ;
import * as React from 'react' ;
import { styled } from '@mui/system' ;
import { Checkbox as BaseCheckbox } from '@base_ui/react/Checkbox' ;
const colors = [ 'Red' , 'Green' , 'Blue' ];
export default function UnstyledCheckboxIndeterminateGroup () {
const [ checkedValues , setCheckedValues ] = React . useState ([ 'Green' ]);
const isChecked = checkedValues . length === colors . length ;
const isIndeterminate =
checkedValues . length !== colors . length && checkedValues . length > 0 ;
const id = React . useId ();
return (
< div style = {{ display: 'flex' , flexDirection: 'column' }}>
< ListRoot >
< Checkbox
id = { id }
aria-controls = { colors . map (( color ) => `${ id }-${ color }` ). join ( ' ' )}
indeterminate = { isIndeterminate }
checked = { isChecked }
onCheckedChange = {( checked ) => {
setCheckedValues ( checked ? colors : []);
}}
>
< Indicator >
{ isIndeterminate ? < HorizontalRuleIcon /> : < CheckIcon />}
</ Indicator >
</ Checkbox >
< Label htmlFor = { id } onMouseDown = {( event ) => event . preventDefault ()}>
Colors
</ Label >
</ ListRoot >
< List >
{ colors . map (( color ) => (
< ListItem key = { color }>
< Checkbox
id = { `${ id }-${ color }` }
checked = { checkedValues . includes ( color )}
onCheckedChange = {( checked ) => {
const newCheckedValues = [ ... checkedValues ];
if ( checked ) {
newCheckedValues . push ( color );
} else {
newCheckedValues . splice ( newCheckedValues . indexOf ( color ), 1 );
}
setCheckedValues ( newCheckedValues );
}}
>
< Indicator >
< CheckIcon />
</ Indicator >
</ Checkbox >
< Label
htmlFor = { `${ id }-${ color }` }
onMouseDown = {( event ) => event . preventDefault ()}
>
{ color }
</ Label >
</ ListItem >
))}
</ List >
</ div >
);
}
const blue = {
400 : '#3399FF' ,
600 : '#0072E6' ,
800 : '#004C99' ,
};
const grey = {
100 : '#E5EAF2' ,
400 : '#B0B8C4' ,
800 : '#303740' ,
};
const Checkbox = styled ( BaseCheckbox . Root )(
({ theme }) => `
width: 24px;
height: 24px;
padding: 0;
border-radius: 4px;
border: 2px solid ${ blue [ 600 ] };
background: none;
transition-property: background, border-color;
transition-duration: 0.15s;
outline: none;
&[data-disabled] {
opacity: 0.4;
cursor: not-allowed;
}
&:focus-visible {
outline: 2px solid ${ theme . palette . mode === 'dark' ? blue [ 800 ] : blue [ 400 ] };
outline-offset: 2px;
}
&[data-checked], &[data-indeterminate] {
border-color: transparent;
background: ${ blue [ 600 ] };
}
` ,
);
const HorizontalRuleIcon = styled ( function HorizontalRuleIcon (
props : React . SVGProps < SVGSVGElement >,
) {
return (
< svg
xmlns = "http://www.w3.org/2000/svg"
{ ... props }
width = "24"
height = "24"
viewBox = "0 0 24 24"
fill = "none"
>
< path d = "M4 11h16v2H4z" fill = "currentColor" />
</ svg >
);
}) `
height: 100%;
width: 100%;
` ;
const CheckIcon = styled ( function CheckIcon ( props : React . SVGProps < SVGSVGElement >) {
return (
< svg
xmlns = "http://www.w3.org/2000/svg"
{ ... props }
width = "24"
height = "24"
viewBox = "0 0 24 24"
fill = "none"
>
< path
d = "M9 16.17 4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"
fill = "currentColor"
/>
</ svg >
);
}) `
height: 100%;
width: 100%;
` ;
const Indicator = styled ( BaseCheckbox . Indicator ) `
height: 100%;
display: inline-block;
visibility: hidden;
color: ${ grey [ 100 ] };
&[data-checked],
&[data-indeterminate] {
visibility: visible;
}
` ;
const ListRoot = styled ( 'div' ) `
display: flex;
align-items: center;
margin-bottom: 8px;
` ;
const List = styled ( 'ul' ) `
list-style: none;
padding: 0;
margin: 0;
margin-left: 32px;
` ;
const ListItem = styled ( 'li' ) `
display: flex;
align-items: center;
&:not(:last-child) {
margin-bottom: 8px;
}
` ;
const Label = styled ( 'label' )(
({ theme }) => `
padding-left: 8px;
color: ${ theme . palette . mode === 'dark' ? grey [ 400 ] : grey [ 800 ] };
` ,
);
It's a visual-only state, so its internal checked
state can still be changed.
Use the render
prop to override the rendered checkbox or indicator element with your own components.
< Checkbox.Root render = {( props ) => < MyCheckbox { ... props } />}>
< Checkbox.Indicator render = {( props ) => < MyCheckboxIndicator { ... props } />} />
</ Checkbox.Root >
Ensure the Checkbox has an accessible name via a <label>
element.
< Checkbox.Root id = "my-checkbox" >
< Checkbox.Indicator />
</ Checkbox.Root >
< label htmlFor = "my-checkbox" >
My label
</ label >
API Reference CheckboxRoot The foundation for building custom-styled checkboxes.
Prop Type Default Description checked
bool
undefined
If true
, the component is checked. className
union
Class names applied to the element or a function that returns them based on the component's state. defaultChecked
bool
false
The default checked state. Use when the component is not controlled. disabled
bool
false
If true
, the component is disabled. indeterminate
bool
false
If true
, the checkbox will be indeterminate. inputRef
union
The ref to the input element. name
string
undefined
Name of the underlying input element. onCheckedChange
func
Callback fired when the checked state is changed. parent
bool
false
If true
, the checkbox is a parent checkbox for a group of child checkboxes. readOnly
bool
false
If true
, the component is read only. render
union
A function to customize rendering of the component. required
bool
false
If true
, the input
element is required.
CheckboxIndicator The indicator part of the Checkbox.
Prop Type Default Description className
union
Class names applied to the element or a function that returns them based on the component's state. keepMounted
bool
true
If true
, the indicator stays mounted when unchecked. Useful for CSS animations. render
union
A function to customize rendering of the component.