Calendar
This page describes how people can use date views with Material UI and how they can build custom date views.
Usage with only days
Without Material UI
The user can use the <Calendar.DaysGrid />
, <Calendar.DaysGridHeader />
, <Calendar.DaysGridHeaderCell />
, <Calendar.DaysGridBody />
, <Calendar.DaysWeekRow />
and <Calendar.DaysCell />
components to create a grid of days:
import { Calendar } from '@base-ui-components/react-x-date-pickers/calendar';
<Calendar.Root value={value} onValueChange={setValue}>
{({ visibleMonth }) => (
<React.Fragment>
<div>
<Calendar.SetVisibleMonth target="previous">◀</Calendar.SetVisibleMonth>
{visibleMonth.format('MMMM YYYY')}
<Calendar.SetVisibleMonth target="next">▶</Calendar.SetVisibleMonth>
</div>
<Calendar.DaysGrid>
<Calendar.DaysGridHeader>
{({ days }) =>
days.map((day) => (
<Calendar.DaysGridHeaderCell value={day} key={day.toString()} />
))
}
</Calendar.DaysGridHeader>
<Calendar.DaysGridBody>
{({ weeks }) =>
weeks.map((week) => (
<Calendar.DaysWeekRow value={week}>
{({ days }) =>
days.map((day) => (
<Calendar.DaysCell value={day} key={day.toString()} />
))
}
</Calendar.DaysWeekRow>
))
}
</Calendar.DaysGridBody>
</Calendar.DaysGrid>
</React.Fragment>
)}
</Calendar.Root>;
With Material UI
The user can use the <DateCalendar />
and limit the views:
import { DateCalendar } from '@mui/x-date-pickers/DateCalendar';
<DateCalendar views={['day']} value={value} onChange={setValue} />;
Usage with only months
Without Material UI
List layout
The user can use the <Calendar.MonthsList />
and <Calendar.MonthsCell />
components to create a list of months and utility components like <Calendar.SetVisibleYear />
to create a header to navigate across the years:
import { Calendar } from '@base-ui-components/react-x-date-pickers/calendar';
<Calendar.Root value={value} onValueChange={setValue}>
{({ visibleMonth }) => (
<React.Fragment>
<div>
<Calendar.SetVisibleYear target="previous">◀</Calendar.SetVisibleYear>
{visibleMonth.format('YYYY')}
<Calendar.SetVisibleYear target="next">▶</Calendar.SetVisibleYear>
</div>
<Calendar.MonthsList>
{({ months }) =>
months.map((month) => (
<Calendar.MonthsCell value={month} key={month.toString()} />
))
}
</Calendar.MonthsList>
</React.Fragment>
)}
</Calendar.Root>;
Grid layout
The user can use the <Calendar.MonthsGrid />
and <Calendar.MonthsCell />
components to create a grid of months and utility components like <Calendar.SetVisibleYear />
to create a header to navigate across the years:
import { Calendar } from '@base-ui-components/react-x-date-pickers/calendar';
<Calendar.Root value={value} onValueChange={setValue}>
{({ visibleMonth }) => (
<React.Fragment>
<div>
<Calendar.SetVisibleYear target="previous">◀</Calendar.SetVisibleYear>
{visibleMonth.format('YYYY')}
<Calendar.SetVisibleYear target="next">▶</Calendar.SetVisibleYear>
</div>
<Calendar.MonthsGrid cellsPerRow={2}>
{({ months }) =>
months.map((month) => (
<Calendar.MonthsCell value={month} key={month.toString()} />
))
}
</Calendar.MonthsGrid>
</React.Fragment>
)}
</Calendar.Root>;
With Material UI
The user can use the <MonthCalendar />
component:
import { MonthCalendar } from '@mui/x-date-pickers/MonthCalendar';
<MonthCalendar value={value} onChange={setValue}>
Usage with only years
Without Material UI
List layout
The user can use the <Calendar.YearsList />
and <Calendar.YearsCell />
components to create a list of years:
import { Calendar } from '@base-ui-components/react-x-date-pickers/calendar';
<Calendar.Root value={value} onValueChange={setValue}>
<Calendar.YearsList>
{({ years }) =>
years.map((year) => <Calendar.YearsCell value={year} key={year.toString()} />)
}
</Calendar.YearsList>
</Calendar.Root>;
Grid layout
The user can use the <Calendar.YearsGrid />
and <Calendar.YearsCell />
components to create a grid of years:
import { Calendar } from '@base-ui-components/react-x-date-pickers/calendar';
<Calendar.Root value={value} onValueChange={setValue}>
<Calendar.YearsGrid cellsPerRow={3}>
{({ years }) =>
years.map((year) => <Calendar.YearsCell value={year} key={year.toString()} />)
}
</Calendar.YearsGrid>
</Calendar.Root>;
With Material UI
The user can use the <YearCalendar />
component:
import { YearCalendar } from '@mui/x-date-pickers/YearCalendar';
<YearCalendar value={value} onChange={setValue}>
Day + month + years
Without Material UI
import { Calendar } from '@base-ui-components/react-x-date-pickers/calendar';
function DateCalendar() {
const [value, setValue] = React.useState(null);
const [activeSection, setActiveSection] = React.useState<'day' | 'month' | 'year'>(
'day',
);
const handleValueChange = (newValue, context) => {
if (context.section === 'month' || context.section === 'year') {
setActiveSection('day');
}
setValue(newValue);
};
return (
<Calendar.Root value={value} onValueChange={handleValueChange}>
<div>{/** See calendar header documentation */}</div>
{activeSection === 'day' && (
<Calendar.DaysGrid>
<Calendar.DaysGridHeader>
{({ days }) =>
days.map((day) => (
<Calendar.DaysGridHeaderCell value={day} key={day.toString()} />
))
}
</Calendar.DaysGridHeader>
<Calendar.DaysGridBody>
{({ weeks }) =>
weeks.map((week) => (
<Calendar.DaysWeekRow value={week} key={week.toString()}>
{({ days }) =>
days.map((day) => (
<Calendar.DaysCell value={day} key={day.toString()} />
))
}
</Calendar.DaysWeekRow>
))
}
</Calendar.DaysGridBody>
</Calendar.DaysGrid>
)}
{activeSection === 'month' && (
<Calendar.MonthsList>
{({ months }) =>
months.map((month) => (
<Calendar.MonthsCell value={month} key={month.toString()} />
))
}
</Calendar.MonthsList>
)}
{activeSection === 'year' && (
<Calendar.YearsList>
{({ years }) =>
years.map((year) => (
<Calendar.YearsCell value={year} key={year.toString()} />
))
}
</Calendar.YearsList>
)}
</Calendar.Root>
);
}
With Material UI
The user can use the <DateCalendar />
component and add the month view:
import { DateCalendar } from '@mui/x-date-pickers/DateCalendar';
<DateCalendar value={value} onChange={setValue}>
Calendar header
Without Material UI
The user can use the <Calendar.SetVisibleMonth />
to build basically any kind of header they could think of:
Very basic header (without month and year UI)
import { Calendar } from '@base-ui-components/react-x-date-pickers/calendar';
function CalendarHeader() {
const { visibleMonth } = useCalendarContext();
return (
<div>
<Calendar.SetVisibleMonth target="previous">◀</Calendar.SetVisibleMonth>
{visibleMonth.format('MMMM YYYY')}
<Calendar.SetVisibleMonth target="next">▶</Calendar.SetVisibleMonth>
</div>
);
}
MD2 header (MUI X implementation)
import {
Calendar,
useCalendarContext,
} from '@base-ui-components/react-x-date-pickers/calendar';
function CalendarHeader(props: {
activeSection: 'day' | 'month' | 'year';
onActiveSectionChange: (activeSection: 'day' | 'month' | 'year') => void;
}) {
const { activeSection, onActiveSectionChange } = props;
const { visibleMonth } = useCalendarContext();
return (
<div>
<div
onClick={() =>
onActiveSectionChange(activeSection === 'year' ? 'month' : 'year')
}
>
{visibleMonth.format('MMMM YYYY')}
{activeSection === 'year' ? '▲' : '▼'}
</div>
{activeSection === 'day' && (
<div>
<Calendar.SetVisibleMonth target="previous">◀</Calendar.SetVisibleMonth>
<Calendar.SetVisibleMonth target="next">▶</Calendar.SetVisibleMonth>
</div>
)}
</div>
);
}
The activeSection
and onActiveSectionChange
needs to be passed by the parent component:
function DateCalendar() {
const [activeSection, setActiveSection] = React.useState<'day' | 'month' | 'year'>(
'day',
);
return (
<Calendar.Root>
<Header
activeSection={activeSection}
onActiveSectionChange={setActiveSection}
/>
{/** Rest of the UI */}
</Calendar.Root>
);
}
MD3 header
import { Calendar } from '@base-ui-components/react-x-date-pickers/calendar';
function CalendarHeader(props: {
activeSection: 'day' | 'month' | 'year';
onActiveSectionChange: (activeSection: 'day' | 'month' | 'year') => void;
}) {
const { activeSection, onActiveSectionChange } = props;
const { visibleMonth } = useCalendarContext();
return (
<div>
<div>
{activeSection === 'day' && (
<Calendar.SetVisibleMonth target="previous">◀</Calendar.SetVisibleMonth>
)}
<button
onClick={() =>
onActiveSectionChange(activeSection === 'year' ? 'day' : 'year')
}
disabled={activeSection === 'month'}
>
{visibleMonth.format('MMMM')}
</button>
{activeSection === 'day' && (
<Calendar.SetVisibleMonth target="next">▶</Calendar.SetVisibleMonth>
)}
</div>
<div>
{activeSection === 'day' && (
<Calendar.SetVisibleYear target="previous">◀</Calendar.SetVisibleYear>
)}
<button
onClick={() =>
onActiveSectionChange(activeSection === 'month' ? 'day' : 'month')
}
disabled={activeSection === 'year'}
>
{visibleMonth.format('YYYY')}
</button>
{activeSection === 'day' && (
<Calendar.SetVisibleYear target="next">▶</Calendar.SetVisibleYear>
)}
</div>
</div>
);
}
Header with dropdown for months and years
The user can create a custom header where the month and the year editing is done through a menu, while the day calendar is always visible below:
import { Menu } from '@base-ui-components/react/menu';
import { Calendar } from '@base-ui-components/react-x-date-pickers/calendar';
function CalendarHeader() {
const { visibleMonth, activeSection } = useCalendarContext();
return (
<div>
<Menu.Root>
<Menu.Trigger>{visibleMonth.format('MMMM')}</Menu.Trigger>
<Menu.Positioner>
<Calendar.MonthsList alwaysVisible render={<Menu.Popup />}>
{({ months }) =>
months.map((month) => (
<Calendar.MonthsCell value={month} key={month.toString()} />
))
}
</Calendar.MonthsList>
</Menu.Positioner>
</Menu.Root>
<Menu.Root>
<Menu.Trigger>{visibleMonth.format('YYYY')}</Menu.Trigger>
<Menu.Positioner>
<Calendar.YearsList alwaysVisible render={<Menu.Popup />}>
{({ years }) =>
years.map((year) => (
<Calendar.MonthsCell value={year} key={year.toString()} />
))
}
</Calendar.YearsList>
</Menu.Positioner>
</Menu.Root>
</div>
);
}
With Material UI
The user can use slots to override part of the UI in self-contained components:
<DateCalendar slots={{ calendarHeader: CustomCalendarHeader }} />
The <CustomCalendarHeader />
component can be built in a few different ways:
From scratch:
This is mostly viable for components that don't interact a lot with the picker state. For example, if someone wants to build a custom header for their calendar that just displays the current month, they could do it from scratch:
import { useCalendarContext } from '@base-ui-components/react-x-date-pickers/calendar'; function CustomCalendarHeader() { const { currentMonth } = useCalendarContext(); return <div>{currentMonth.format('MMMM YYYY')}</div>; }
This is not the recommended way, but nothing prevents it.
Using the primitives exposed by
@base-ui-components/react-x-date-pickers/calendar
:If the user wants to totally own the styling of this part of the UI (because the UI is really different from the default one), he can use components like
<Calendar.SetVisibleMonth />
only for this part of the UI while still using@mui/x-date-pickers
for everything he doesn't want to deeply customize:import { Calendar } from '@base-ui-components/react-x-date-pickers/calendar'; function CustomCalendarHeader() { const { visibleMonth } = useCalendarContext(); return ( <div> <Calendar.SetVisibleMonth target="previous">◀</Calendar.SetVisibleMonth> {visibleMonth.format('MMMM YYYY')} <Calendar.SetVisibleMonth target="next">▶</Calendar.SetVisibleMonth> </div> ); }
Using the
PickerCalendarHeader*
components exposed by@mui/x-date-pickers/PickerCalendarHeader
:import { PickersCalendarHeaderRoot, PickersCalendarHeaderLabel, } from '@mui/x-date-pickers/PickersCalendarHeader'; function CustomCalendarHeader() { return ( <PickersCalendarHeaderRoot> <PickerCalendarHeaderLabelContainer> <PickersCalendarHeaderLabel format="MMMM YYYY" /> </PickerCalendarHeaderLabelContainer> </PickersCalendarHeaderRoot> ); }
Display multiple months
Without Material UI
The user can use the offset
prop of the <Calendar.DaysGrid />
component to render months with an offset compared to the currently visible month:
import { Calendar } from '@base-ui-components/react-x-date-pickers/calendar';
function CalendarGrid({ offset }) {
return (
<Calendar.DaysGrid offset={offset}>
<Calendar.DaysGridHeader>
{({ days }) =>
days.map((day) => (
<Calendar.DaysGridHeaderCell value={day} key={day.toString()} />
))
}
</Calendar.DaysGridHeader>
<Calendar.DaysGridBody>
{({ weeks }) =>
weeks.map((week) => (
<Calendar.DaysWeekRow value={week} key={week.toString()}>
{({ days }) =>
days.map((day) => (
<Calendar.DaysCell value={day} key={day.toString()} />
))
}
</Calendar.DaysWeekRow>
))
}
</Calendar.DaysGridBody>
</Calendar.DaysGrid>
);
}
<Calendar.Root value={value} onValueChange={setValue}>
<div>{/** See demo below for the navigation with multiple months */}</div>
<div>
<CalendarGrid offset={0} />
<CalendarGrid offset={1} />
</div>
</Calendar.Root>;
Month navigation with multiple months
There is two way to navigate to the next / previous months:
- With the
<Calendar.SetVisibleMonth />
button - With the arrow navigation when the target is not in the current month
When rendering multiple months, both those navigation technic only navigate one month at the time.
For example, if you were rendering May and June, pressing <Calendar.SetVisibleMonth target="next" />
will render June and July.
The user can use the monthPageSize
prop on the <Calendar.Root />
component to customize this behavior.
If the prop receives a number, it will move by the amount of month both for <Calendar.SetVisibleMonth />
and for arrow navigation:
import { Calendar } from '@base-ui-components/react-x-date-pickers/calendar';
function CalendarHeader() {
const { visibleMonth } = useCalendarContext();
return (
<Calendar.Root monthPageSize={2}>
<div>
<Calendar.SetVisibleMonth target="previous">◀</Calendar.SetVisibleMonth>
{visibleMonth.format('MMMM YYYY')}
{' – '}
{visibleMonth.add(1, 'month').format('MMMM YYYY')}
<Calendar.SetVisibleMonth target="next">▶</Calendar.SetVisibleMonth>
</div>
<CalendarGrid offset={0} />
<CalendarGrid offset={1} />
</Calendar.Root>
);
}
But the user can also distinguish both behaviors by providing an object:
import { Calendar } from '@base-ui-components/react-x-date-pickers/calendar';
function CalendarHeader() {
const { visibleMonth } = useCalendarContext();
return (
<Calendar.Root monthPageSize={{ keyboard: 2, button: 1 }}>
<div>
<Calendar.SetVisibleMonth target="previous">◀</Calendar.SetVisibleMonth>
{visibleMonth.format('MMMM YYYY')}
{' – '}
{visibleMonth.add(1, 'month').format('MMMM YYYY')}
<Calendar.SetVisibleMonth target="next">▶</Calendar.SetVisibleMonth>
</div>
<CalendarGrid offset={0} />
<CalendarGrid offset={1} />
</Calendar.Root>
);
}
With Material UI
This is currently not doable.
Display week number
Without Material UI
The user can add custom elements to add the week number to the grid:
<Calendar.DaysGrid>
<Calendar.DaysGridHeader>
{({ days }) => (
<React.Fragment>
<span role="columnheader" aria-label="Week number">
#
</span>
{days.map((day) => (
<Calendar.DaysGridHeaderCell value={day} key={day.toString()} />
))}
</React.Fragment>
)}
</Calendar.DaysGridHeader>
<Calendar.DaysGridBody>
{({ weeks }) =>
weeks.map((week) => (
<Calendar.DaysWeekRow value={week} key={week.toString()}>
{({ days }) => (
<React.Fragment>
<span role="rowheader" aria-label={`Week ${days[0].week()}`}>
{days[0].week()}
</span>
{days.map((day) => (
<Calendar.DaysCell value={day} key={day.toString()} />
))}
</React.Fragment>
)}
</Calendar.DaysWeekRow>
))
}
</Calendar.DaysGridBody>
</Calendar.DaysGrid>
With Material UI
The user can use the <DateCalendar />
with the displayWeekNumber
prop:
import { DateCalendar } from '@mui/x-date-pickers/DateCalendar';
<DateCalendar displayWeekNumber views={['day']} value={value} onChange={setValue} />;
Anatomy of Calendar.*
Calendar.Root
Top level component that wraps the other components.
Props
Extends
React.HTMLAttributes<HTMLDivElement>
Value props:
value
,defaultValue
,referenceDate
,onValueChange
,onError
andtimezone
.Same typing and behavior as today (just
onChange
becomesonValueChange
)Validation props:
maxDate
,minDate
,disableFuture
,disablePast
,shouldDisableDate
,shouldDisableMonth
,shouldDisableYear
.Same typing and behavior as today.
Form props:
disabled
,readOnly
.Same typing and behavior as today.
autoFocus
:boolean
monthPageSize
:number | { keyboard: number, button: number }
, default:1
. The amount of months to navigate by in the day grid when pressing<Calendar.SetVisibleMonth />
or with arrow navigation.children
:React.ReactNode | (contextValue: CalendarContextValue) => React.ReactNode
Calendar.SetVisibleMonth
Renders a button to go to the previous or the next month. It does not modify the value it only navigates to the target month.
Props
Extends
React.HTMLAttributes<HTMLButtonElement>
target
:'previous' | 'next' | PickerValidDate
Calendar.SetVisibleYear
Renders a button to go to the previous or the next month. It does not modify the value it only navigates to the target year.
Props
Extends
React.HTMLAttributes<HTMLButtonElement>
target
:'previous' | 'next' | PickerValidDate
Calendar.DaysGrid
Top level component to pick a day.
Props
Extends
React.HTMLAttributes<HTMLDivElement>
fixedWeekNumber
:number
Calendar.DaysGridHeader
Renders the header of the day grid.
It expects a function as its children, which receives the list of days to render as a parameter:
<Calendar.DaysGridHeader>
{({ days }) =>
days.map((day) => (
<Calendar.DaysGridHeaderCell value={day} key={day.toString()} />
))
}
</Calendar.DaysGridHeader>
Props
Extends
React.HTMLAttributes<HTMLDivElement>
children
:(params: { days: PickerValidDate[] }) => React.ReactNode
Calendar.DaysGridBody
Renders the content all the days in a month (it is the DOM element that should contain all the weeks).
It expects a function as its children, which receives the list of weeks to render as a parameter:
<Calendar.DaysGridBody>
{({ weeks }) =>
weeks.map((week) => <Calendar.DaysWeekRow value={week} key={week.toString()} />)
}
</Calendar.DaysGridBody>
Props
Extends
React.HTMLAttributes<HTMLDivElement>
children
:(params: { weeks: PickerValidDate[] }) => React.ReactNode
Calendar.DaysWeekRow
Renders the content all the days in a week.
It expects a function as its children, which receives the list of days to render and the week number as a parameter:
<Calendar.DaysWeekRow>
{({ days }) =>
days.map((day) => <Calendar.DaysCell value={day} key={day.toString()} />)
}
</Calendar.DaysWeekRow>
Props
Extends
React.HTMLAttributes<HTMLDivElement>
value
:{ value: PickerValidDate }
- requiredchildren
:(params: { days: PickerValidDate[], week: PickerValidDate }) => React.ReactNode
Calendar.DaysCell
Renders the cell for a single day.
Props
Extends
React.HTMLAttributes<HTMLButtonElement>
value
:PickerValidDate
- required
Calendar.MonthsList
Top level component to pick a month.
It expects a function as its children, which receives the list of the months to render as a parameter:
<Calendar.MonthsList>
{({ months }) =>
months.map((month) => (
<Calendar.MonthsCell value={month} key={month.toString()} />
))
}
</Calendar.MonthsList>
This component takes care of the keyboard navigation (for example Arrow Up).
Props
Extends
React.HTMLAttributes<HTMLDivElement>
children
:(params: { months: PickerValidDate[] }) => React.ReactNode
itemsOrder
:'asc' | 'desc'
, default:'asc'
.alwaysVisible
:boolean
, default:false
. By default this component is only rendered when the active section is"month"
.
Calendar.MonthsGrid
Top level component to pick a month, when the layout is a grid.
It expects a function as its children, which receives the list of months to render as a parameter:
<Calendar.MonthsGrid cellsPerRow={2}>
{({ months }) =>
months.map((month) => (
<Calendar.MonthsCell value={month} key={month.toString()} />
))
}
</Calendar.MonthsGrid>
This component takes care of the keyboard navigation (for example Arrow Up).
Props
Extends
React.HTMLAttributes<HTMLDivElement>
children
:(params: { months: PickerValidDate[] }) => React.ReactNode
cellsOrder
:'asc' | 'desc'
, default:'asc'
.cellsPerRow
:number
required.
Calendar.MonthsCell
Renders the cell for a single month.
Props
Extends
React.HTMLAttributes<HTMLButtonElement>
value
:PickerValidDate
- required.
Calendar.YearsList
Top level component to pick a year when the layout is a list.
It expects a function as its children, which receives the list of years to render as a parameter:
<Calendar.YearsList>
{({ years }) =>
years.map((year) => <Calendar.YearsCell value={year} key={year.toString()} />)
}
</Calendar.YearsList>
This component takes care of the keyboard navigation (for example Arrow Up).
Props
Extends
React.HTMLAttributes<HTMLDivElement>
children
:(params: { years: PickerValidDate[] }) => React.ReactNode
cellsOrder
:'asc' | 'desc'
, default:'asc'
.
Calendar.YearsGrid
Top level component to pick a year when the layout is a grid.
It expects a function as its children, which receives the list of years to render as a parameter:
<Calendar.YearsGrid cellsPerRo={3}>
{({ years }) =>
years.map((year) => <Calendar.YearsCell value={year} key={year.toString()} />)
}
</Calendar.YearsGrid>
This component takes care of the keyboard navigation (for example Arrow Up).
Props
Extends
React.HTMLAttributes<HTMLDivElement>
children
:(params: { years: PickerValidDate[] }) => React.ReactNode
cellsOrder
:'asc' | 'desc'
, default:'asc'
.cellsPerRow
:number
required.
Calendar.YearsCell
Renders the cell for a single year.
Props
Extends
React.HTMLAttributes<HTMLButtonElement>
value
:PickerValidDate
- required.