React Calendar Custom Day Cells
Get started with this exampleUsage
theme="ios" You can turn auto theming on and let it decide based on the platform.
Usage
themeVariant="light" You can turn auto dark mode on based on system settings.
Usage
touchUi={true}
// touch interaction is enabled Use touch interaction on mobile or pointer interaction on desktop.
Usage
locale={localeEn} Selecting the locale loads the localized settings and the translated UI text.
Custom Day Cells
Add custom content to the day cells of the calendar. This can be dynamic content based on the date like an activity tracker that uses daily move data from an array of records.
Use the renderDayContent function to return custom markup that will be used when the day cells are being rendered.
Demo
Code
View code on GitHub
import { Datepicker, setOptions } from '@mobiscroll/react';
setOptions({
locale: ,
theme: ' ',
themeVariant: ' '
});
const activities = [
{
date: '2026-10-30T00:00',
move: 200,
exercise: 360,
stand: 180,
},
{
date: '2026-10-31T00:00',
move: 360,
exercise: 150,
stand: 230,
},
{
date: '2026-11-01T00:00',
move: 180,
exercise: 200,
stand: 280,
},
{
date: '2026-11-02T00:00',
move: 120,
exercise: 150,
stand: 200,
},
{
date: '2026-11-03T00:00',
move: 320,
exercise: 180,
stand: 100,
},
{
date: '2026-11-04T00:00',
move: 120,
exercise: 100,
stand: 200,
},
{
date: '2026-11-05T00:00',
move: 230,
exercise: 170,
stand: 330,
},
{
date: '2026-11-06T00:00',
move: 320,
exercise: 260,
stand: 80,
},
{
date: '2026-11-07T00:00',
move: 200,
exercise: 140,
stand: 180,
},
{
date: '2026-11-08T00:00',
move: 360,
exercise: 300,
stand: 160,
},
{
date: '2026-11-09T00:00',
move: 80,
exercise: 360,
stand: 360,
},
{
date: '2026-11-10T00:00',
move: 220,
exercise: 170,
stand: 290,
},
{
date: '2026-11-11T00:00',
move: 120,
exercise: 40,
stand: 100,
},
{
date: '2026-11-12T00:00',
move: 120,
exercise: 200,
stand: 80,
},
];
function App() {
const getDeg = (nr) => ({
rotate1: nr > 180 ? 180 : nr,
rotate2: nr > 180 ? nr - 180 : 0,
});
const getTransform = (rotate) => 'rotateZ(' + rotate + 'deg)';
const customDay = (args) => {
const a = activities.find((obj) => +new Date(obj.date) === +args.date);
return (
<div className="screen">
<div className={a ? 'dial move' : ''}>
<div className="dial-background" style={{ background: a ? '#752a2a' : '' }}></div>
<div className="dial-container container1">
<div className="wedge" style={{ transform: a ? getTransform(getDeg(a.move).rotate1) : '' }}></div>
</div>
<div className="dial-container container2">
<div className="wedge" style={{ transform: a ? getTransform(getDeg(a.move).rotate2) : '' }}></div>
</div>
<div className="marker start"></div>
<div className="marker end" style={{ transform: a ? getTransform(a.move) : '' }}></div>
</div>
<div className={a ? 'dial exercise' : ''}>
<div className="dial-background" style={{ background: a ? '#4a6915' : '' }}></div>
<div className="dial-container container1">
<div className="wedge" style={{ transform: a ? getTransform(getDeg(a.exercise).rotate1) : '' }}></div>
</div>
<div className="dial-container container2">
<div className="wedge" style={{ transform: a ? getTransform(getDeg(a.exercise).rotate2) : '' }}></div>
</div>
<div className="marker start"></div>
<div className="marker end" style={{ transform: a ? getTransform(a.exercise) : '' }}></div>
</div>
<div className={a ? 'dial stand' : ''}>
<div className="dial-background" style={{ background: a ? '#157b76' : '' }}></div>
<div className="dial-container container1">
<div className="wedge" style={{ transform: a ? getTransform(getDeg(a.stand).rotate1) : '' }}></div>
</div>
<div className="dial-container container2">
<div className="wedge" style={{ transform: a ? getTransform(getDeg(a.stand).rotate2) : '' }}></div>
</div>
<div className="marker start"></div>
<div className="marker end" style={{ transform: a ? getTransform(a.stand) : '' }}></div>
</div>
</div>
);
};
return (
<div>
<Datepicker controls={['calendar']} touchUi={true} display="inline" renderDayContent={customDay} />
</div>
);
}
export default App;
import {
Datepicker,
// MbscCalendarDayData,
setOptions ,
} from '@mobiscroll/react';
import { FC } from 'react';
setOptions({
locale: ,
theme: ' ',
themeVariant: ' '
});
const activities = [
{
date: '2026-10-30T00:00',
move: 200,
exercise: 360,
stand: 180,
},
{
date: '2026-10-31T00:00',
move: 360,
exercise: 150,
stand: 230,
},
{
date: '2026-11-01T00:00',
move: 180,
exercise: 200,
stand: 280,
},
{
date: '2026-11-02T00:00',
move: 120,
exercise: 150,
stand: 200,
},
{
date: '2026-11-03T00:00',
move: 320,
exercise: 180,
stand: 100,
},
{
date: '2026-11-04T00:00',
move: 120,
exercise: 100,
stand: 200,
},
{
date: '2026-11-05T00:00',
move: 230,
exercise: 170,
stand: 330,
},
{
date: '2026-11-06T00:00',
move: 320,
exercise: 260,
stand: 80,
},
{
date: '2026-11-07T00:00',
move: 200,
exercise: 140,
stand: 180,
},
{
date: '2026-11-08T00:00',
move: 360,
exercise: 300,
stand: 160,
},
{
date: '2026-11-09T00:00',
move: 80,
exercise: 360,
stand: 360,
},
{
date: '2026-11-10T00:00',
move: 220,
exercise: 170,
stand: 290,
},
{
date: '2026-11-11T00:00',
move: 120,
exercise: 40,
stand: 100,
},
{
date: '2026-11-12T00:00',
move: 120,
exercise: 200,
stand: 80,
},
];
const App: FC = () => {
const getDeg = (nr: number) => ({
rotate1: nr > 180 ? 180 : nr,
rotate2: nr > 180 ? nr - 180 : 0,
});
const getTransform = (rotate: number) => 'rotateZ(' + rotate + 'deg)';
const customDay = (args: { date: Date }) => {
const a = activities.find((obj) => +new Date(obj.date) === +args.date);
return (
<div className="screen">
<div className={a ? 'dial move' : ''}>
<div className="dial-background" style={{ background: a ? '#752a2a' : '' }}></div>
<div className="dial-container container1">
<div className="wedge" style={{ transform: a ? getTransform(getDeg(a.move).rotate1) : '' }}></div>
</div>
<div className="dial-container container2">
<div className="wedge" style={{ transform: a ? getTransform(getDeg(a.move).rotate2) : '' }}></div>
</div>
<div className="marker start"></div>
<div className="marker end" style={{ transform: a ? getTransform(a.move) : '' }}></div>
</div>
<div className={a ? 'dial exercise' : ''}>
<div className="dial-background" style={{ background: a ? '#4a6915' : '' }}></div>
<div className="dial-container container1">
<div className="wedge" style={{ transform: a ? getTransform(getDeg(a.exercise).rotate1) : '' }}></div>
</div>
<div className="dial-container container2">
<div className="wedge" style={{ transform: a ? getTransform(getDeg(a.exercise).rotate2) : '' }}></div>
</div>
<div className="marker start"></div>
<div className="marker end" style={{ transform: a ? getTransform(a.exercise) : '' }}></div>
</div>
<div className={a ? 'dial stand' : ''}>
<div className="dial-background" style={{ background: a ? '#157b76' : '' }}></div>
<div className="dial-container container1">
<div className="wedge" style={{ transform: a ? getTransform(getDeg(a.stand).rotate1) : '' }}></div>
</div>
<div className="dial-container container2">
<div className="wedge" style={{ transform: a ? getTransform(getDeg(a.stand).rotate2) : '' }}></div>
</div>
<div className="marker start"></div>
<div className="marker end" style={{ transform: a ? getTransform(a.stand) : '' }}></div>
</div>
</div>
);
};
return <Datepicker controls={['calendar']} touchUi={true} display="inline" renderDayContent={customDay} />;
};
export default App;
.screen {
position: relative;
width: 2em;
height: 2em;
margin: 5px auto 10px auto;
}
.screen * {
box-sizing: content-box;
}
.screen .dials {
width: 1em;
height: 1em;
border-radius: 50%;
position: absolute;
top: 0.875em;
left: 0;
}
.screen .dial {
border: 1px solid #000;
border-radius: 50%;
}
/**** DIAL 1 ****/
.dial.move {
position: absolute;
width: 2em;
height: 2em;
}
.dial.move::before {
content: '';
width: 0.75em;
height: 0.75em;
position: absolute;
top: -0.29em;
left: calc(50% - 0.1875em);
z-index: 10;
}
.dial.move::after {
content: '';
background: #000;
border-radius: 50%;
width: 1.5em;
height: 1.5em;
position: absolute;
top: 0.25em;
left: 0.25em;
}
.dial.move .dial-container {
position: absolute;
top: 0;
bottom: 0;
width: 1em;
overflow: hidden;
}
.dial.move .wedge {
width: 1em;
height: 2em;
position: absolute;
top: 0;
left: 0;
}
.dial.move .container1 {
left: 1em;
}
.dial.move .container1 .wedge {
border-radius: 2em 0 0 2em;
background: #fc1d20;
left: -1em;
transform-origin: 100% 50%;
}
.dial.move .container2 {
left: 0;
}
.dial.move .container2 .wedge {
background: #fc1d20;
border-radius: 0 2em 2em 0;
transform-origin: 0 50%;
left: 1em;
}
.dial.move .marker {
width: 0.25em;
height: 0.25em;
background: #fc1d20;
border-radius: 50%;
position: absolute;
top: 0;
left: calc(50% - 0.125em);
}
.dial.move .end {
transform-origin: 50% 1em;
}
/**** DIAL 2 ****/
.dial.exercise {
position: absolute;
width: 1.5em;
height: 1.5em;
top: 0.25em;
left: 0.25em;
}
.dial.exercise::before {
content: '';
width: 0.75em;
height: 0.75em;
position: absolute;
top: -0.29em;
left: calc(50% - 0.1875em);
z-index: 10;
}
.dial.exercise::after {
content: '';
background: #000;
border-radius: 50%;
width: 1em;
height: 1em;
position: absolute;
top: 0.25em;
left: 0.25em;
}
.dial.exercise .dial-container {
position: absolute;
top: 0;
bottom: 0;
width: 0.75em;
overflow: hidden;
}
.dial.exercise .wedge {
border-radius: 50%;
width: 0.75em;
height: 1.5em;
position: absolute;
}
.dial.exercise .container1 {
left: 0.75em;
}
.dial.exercise .container1 .wedge {
border-radius: 2em 0 0 2em;
position: absolute;
background: #9cfc33;
left: -0.75em;
transform-origin: 100% 50%;
}
.dial.exercise .container2 {
left: 0;
}
.dial.exercise .container2 .wedge {
background: #9cfc33;
border-radius: 0 2em 2em 0;
transform-origin: 0 50%;
left: 0.75em;
}
.dial.exercise .marker {
width: 0.25em;
height: 0.25em;
background: #9cfc33;
border-radius: 50%;
position: absolute;
top: 0;
left: calc(50% - 0.125em);
}
.dial.exercise .end {
transform-origin: 45% 0.75em;
}
/**** DIAL 3 ****/
.dial.stand {
position: absolute;
width: 1em;
height: 1em;
top: 0.5em;
left: 0.5em;
}
.dial.stand::before {
content: '';
width: 0.75em;
height: 0.75em;
position: absolute;
top: -0.24em;
left: calc(50% - 0.1875em);
z-index: 10;
}
.dial.stand::after {
content: '';
background: #000;
border-radius: 50%;
width: 0.5em;
height: 0.5em;
position: absolute;
top: 0.25em;
left: 0.25em;
}
.dial.stand .dial-container {
position: absolute;
top: 0;
bottom: 0;
width: 1em;
overflow: hidden;
}
.dial.stand .wedge {
border-radius: 50%;
width: 0.5em;
height: 1em;
position: absolute;
}
.dial.stand .container1 {
left: 0.5em;
}
.dial.stand .container1 .wedge {
position: absolute;
background: #32cbd4;
border-radius: 2em 0 0 2em;
left: -0.5em;
transform-origin: 100% 50%;
}
.dial.stand .container2 {
left: 0;
}
.dial.stand .container2 .wedge {
background: #32cbd4;
border-radius: 0 2em 2em 0;
transform-origin: 0 50%;
left: 0.5em;
}
.dial.stand .marker {
width: 0.25em;
height: 0.25em;
background: #32cbd4;
border-radius: 50%;
position: absolute;
top: 0;
left: calc(50% - 0.125em);
}
.dial.stand .end {
transform-origin: 50% 0.5em;
}
.dial-background {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border-radius: 50%;
}