|Kit

Calendar

Inline month grid on Ark UI's date-picker machine — single or range selection, restriction predicates, event dots, and month/year navigation.

Calendar is the inline month grid — Ark's date-picker machine rendered inline (no trigger/popover), so it's always visible rather than living behind a field. It renders the same grid module DatePicker will embed in its popover once that lands. The value is a native Date | null (or { start, end } under range) — Ark's internal DateValue representation never leaks out.

import { Calendar } from '@42/ui-react/calendar';

<Calendar value={date} onChange={setDate} />

Calendar is a bare widget, like Table — there's no built-in label / description / error. A DatePicker popover (trigger + field wrapper) is where that belongs.

Playground

Component

SpinnerLoading preview

Modes

Props

Code

<Component numOfMonths={1} />

Range & numOfMonths

range selects a { start, end } pair instead of a single date — click once for the start, again for the end. numOfMonths shows that many months side by side; it defaults to 1 regardless of range — pass numOfMonths={2} to show a full range without paging.

26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
import { useState } from 'react';
import { Calendar, type DateRangeValue } from '@42/ui-react/calendar';

const [range, setRange] = useState<DateRangeValue>({ start: null, end: null });

<Calendar range numOfMonths={2} value={range} onChange={setRange} />

Restricting selection

isDateUnavailable takes a plain predicate over a native Date — greyed-out, aria-disabled days the user can't pick. min / max bound the selectable range the same way.

30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
<Calendar isDateUnavailable={(date) => date.getDay() === 0 || date.getDay() === 6} />

A follow-up PR (DatePicker) re-exports a set of composable predicate helpers — disablePast(), disableFuture(), disableWeekends(), beforeDate(d), afterDate(d), disableDates([...]), and anyOf(...) / allOf(...) to combine them — from @42/ui-react/date-picker, built on the exact same conversion boundary Calendar uses internally.

Event dots

renderDay(date, { isUnavailable, isSelected }) decorates a specific day's cell — draw a dot, badge, or any marker from your own event data. It receives a native Date, converted from Ark's internal value at the same timeZone boundary the rest of the component uses.

30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
<Calendar
  renderDay={(date) =>
    hasEvent(date) ? <span className="mt-0.5 block size-1 rounded-full bg-(--c-solid)" /> : null
  }
/>

Month/year navigation

Click the header label to cycle the view — day → month → year — then pick a year, a month, and land back on a day grid. MonthSelect / YearSelect dropdowns in the day view offer a quicker direct jump (shown only when a single month is visible). minView / maxView bound the levels reachable this way, e.g. minView="month" for a picker that never drills into individual days.

<Calendar minView="month" maxView="year" />

Week numbers

26
30
1
2
3
4
5
6
27
7
8
9
10
11
12
13
28
14
15
16
17
18
19
20
29
21
22
23
24
25
26
27
30
28
29
30
31
1
2
3
<Calendar withWeekNumbers />

Fixed height

Months naturally span 4–6 week rows depending on how the days line up, so the grid's height can shift while paging between months. fixedWeeks always renders 6 rows (padding with the adjacent month's days) to keep the height constant.

30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
<Calendar fixedWeeks />

Sizes

Five sizes — xs through xl — matching the rest of the family.

26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5

Code

<Calendar size="xs" defaultValue={new Date()} /><Calendar size="sm" defaultValue={new Date()} /><Calendar size="md" defaultValue={new Date()} /><Calendar size="lg" defaultValue={new Date()} /><Calendar size="xl" defaultValue={new Date()} />

Controlled

Drive the value from state. onChange hands you the next Date | null (or DateRangeValue under range).

'use client';
import { useState } from 'react';
import { Calendar } from '@42/ui-react/calendar';

export function EvaluationDate() {
  const [date, setDate] = useState<Date | null>(null);
  return <Calendar value={date} onChange={setDate} />;
}

Locale & time zone

locale overrides the active locale context (month/day names, first-day-of-week); timeZone overrides the local time zone used for every Date ↔ DateValue conversion at the boundary — default getLocalTimeZone(), not Ark's own machine default of "UTC".

<Calendar locale="fr-FR" timeZone="Europe/Paris" />

Translations

locale drives month/day names and first-day-of-week automatically, but Ark's own built-in accessible names (day cells, prev/next, the view-cycling header, week numbers) are English by default — translations overrides them. It's partial: unset keys keep Ark's own English text.

<Calendar
  locale="fr-FR"
  translations={{
    clearTrigger: 'Effacer la sélection',
    trigger: (open) => (open ? 'Fermer le calendrier' : 'Ouvrir le calendrier'),
  }}
/>

API

Prop

Type

Accessibility

  • role="grid" with full keyboard navigation from Ark — arrows move between days, PageUp / PageDown step a month, Home / End jump to the start/end of the week.
  • Unavailable and out-of-bounds days carry aria-disabled; the selected day (or range) carries aria-selected; today carries aria-current="date".
  • The header's view-cycling label and prev/next buttons are real <button>s with accessible names from Ark's built-in translations — override them with translations (see above).
  • Range selection announces the in-progress start/end via Ark's live region.

On this page