---
title: "React Calendar Custom Day Cells Example | Mobiscroll"
component: "Calendar"
framework: "React"
library_version: "6.2.0"
url: "https://demo.mobiscroll.com/react/calendar/activity-calendar"
---

# Custom Day Cells - React Calendar demo

Add custom activity indicators, availability markers, or daily metrics to each day cell in the inline React calendar date picker. For React and Next.js.

- [Live demo](https://demo.mobiscroll.com/react/calendar/activity-calendar)
- [View React JS code on GitHub](https://github.com/acidb/mobiscroll-demos-react/tree/main/src/demos/datepicker/calendar/activity-calendar)
- [View React TS code on GitHub](https://github.com/acidb/mobiscroll-demos-react-ts/tree/main/src/demos/datepicker/calendar/activity-calendar)
- This demo in other frameworks: [JavaScript](https://demo.mobiscroll.com/calendar/activity-calendar.md), [jQuery](https://demo.mobiscroll.com/jquery/calendar/activity-calendar.md), [Angular](https://demo.mobiscroll.com/angular/calendar/activity-calendar.md), [Vue](https://demo.mobiscroll.com/vue/calendar/activity-calendar.md)

## Description

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`](https://mobiscroll.com/docs/react/datepicker/api#renderer-renderDayContent) function to return custom markup that will be used when the day cells are being rendered.

## What this demo shows

- Shows an inline monthly date picker example with custom day cell for selecting a single date.
- **Inline date picker** Embeds a monthly date picker directly in the page without an input by using the inline display mode.
- **Header navigation** Opens the month and year picker from the label in the upper-left corner. The blue previous and next arrow buttons on the right navigate between months.
- **Month view** Displays a fixed weekday header with abbreviated day names from Sunday through Saturday and arranges the dates in a grid below it.
- **Month navigation** Changes the displayed month when the user clicks and drags the calendar left or right.
- **Day cell states** Highlights a day number with a gray background on hover and a blue background on selection. The current date is highlighted by default and remains blue when another date is selected.
- **Custom day content** Displays an activity indicator below each date using daily move data from an array of records, demonstrating how day cells can render dynamic content based on the date.
- **Adjacent months** Shows dates from the previous and next months in a muted style. Selecting one navigates the calendar to the corresponding month.

## Best for

- **Activity tracking** Showing daily progress, exercise totals, completed habits, or other date-based metrics directly in the calendar.
- **Availability calendars** Indicating available, limited, or unavailable dates before the user makes a selection.
- **Booking and pricing** Displaying daily prices, promotions, capacity, or booking status alongside each date.
- **Deadlines and milestones** Marking due dates, project milestones, or days with scheduled work in a compact monthly view.
- **Operational monitoring** Surfacing daily counts, status indicators, or exceptions so users can identify dates that need attention.

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/react/datepicker/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/react/datepicker/api#opt-themeVariant) - option
- [`controls`](https://mobiscroll.com/docs/react/datepicker/api#opt-controls) - option
- [`touchUi`](https://mobiscroll.com/docs/react/datepicker/api#opt-touchUi) - option
- [`display`](https://mobiscroll.com/docs/react/datepicker/api#opt-display) - option
- [`renderDayContent`](https://mobiscroll.com/docs/react/datepicker/api#renderer-renderDayContent) - renderer

## Code

### JSX

```jsx
import { Datepicker, setOptions } from '@mobiscroll/react';

setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

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;
```

### TSX

```tsx
import {
  Datepicker,
  // MbscCalendarDayData,
  setOptions,
} from '@mobiscroll/react';
import { FC } from 'react';

setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

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;
```

### CSS

```css
.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%;
}
```

## Implementation notes

- Use `controls: ['calendar']`, `display: 'inline'`, and `touchUi` set to `true`.
- React/JS/jQuery: pass `renderDayContent`, a function returning custom markup to inject below/around each day number. Angular: use the `dayContentTemplate` option (an `ng-template` bound with a template reference variable). Vue: use the `#dayContent` slot.
- Inside the renderer, look up the activity/metric value for the rendered day's date from a local data array (matching by date) and render the custom indicator (concentric activity rings, built from plain `div`s with inline `transform`/`background` styles) alongside the default day number - the renderer does not replace the day number itself, only adds to the cell.

## Run it in your project

1. If not signed up yet, or don't own a license, you can [start a free trial](https://mobiscroll.com/starttrial).

2. Clone the repository:

   ```
   git clone https://github.com/acidb/mobiscroll-demos-react.git
   ```

3. If not installed yet, install the Mobiscroll CLI from NPM:

   ```
   npm install -g @mobiscroll/cli
   ```

4. Config Mobiscroll - open a terminal window in the root directory and run:

   ```
   mobiscroll config react
   ```

5. Install dependencies - open a terminal window in the root directory and run:

   ```
   npm install
   ```

6. Run the project:

   ```
   npm run dev
   ```

On a trial, add `--trial` to the config command: `mobiscroll config react --trial`.

Running a demo locally requires a Mobiscroll trial or a license; setup is per framework at https://mobiscroll.com/docs/react/getting-started/installation

## Related demos

- Previous: [Appointment Booking](https://demo.mobiscroll.com/react/calendar/appointment-booking.md) - Build booking date selection interfaces with an inline React calendar — show per-day prices and disable fully booked dates for appointment flows.
