iOS Android
Preview PivotCalendar on your device
Download Expo Go, then scan the QR code to preview native components.
pnpm dlx @docyrus/cli add @docyrus/rn-pivot-calendar
Required Packages (4 packages) pnpm add date-fns react-native-gesture-handler react-native-reanimated tailwind-variants
The public API (PivotCalendar, pivotCalendarVariants, usePivotCalendarController, the helpers and every type) matches the web component. helpers.ts, types.ts and the controller hook are synced copies of the web source. The layout is redesigned for phones:
Toolbar : group-filter icon · ‹ title › · Today on one line, with a horizontally scrollable view switcher (Month / Week / Days / Year) underneath.
Month view : a 7-column grid. Each day shows the first measure (the measure's formatValue, else a compact number such as 1.2K) and a coloured dot for each further non-zero measure. Tapping a day opens a bottom sheet that lists every measure, plus a per-group breakdown when groupBy is set. Each non-zero row fires onCellClick.
Grid views (days-of-week, days-of-month, months-of-year): the group column stays fixed on the left while the date columns scroll horizontally. The view scrolls to today / the current month. A Totals row closes the grid. Each non-zero value cell fires onCellClick.
Groups : the web side panel becomes a bottom sheet with a checkbox list (AvatarThumbnail per group) and an All / Clear toggle. The selection applies to every view.
import {
PivotCalendar ,
type IPivotCalendarGroupBy ,
type IPivotCalendarMeasure
} from '@/components/docyrus-native/pivot-calendar' ;
type Sale = { date : string ; region : string ; amount : number };
const measures : IPivotCalendarMeasure < Sale >[] = [
{
id : 'revenue' ,
label : 'Revenue' ,
aggregate : 'sum' ,
getValue : row => row . amount ,
formatValue : v => `$ ${ ( v / 1000 ). toFixed ( 1 ) } K` ,
color : '#10b981'
},
{ id : 'orders' , label : 'Orders' , aggregate : 'count' , color : '#6366f1' }
];
const groupBy : IPivotCalendarGroupBy < Sale > = {
id : 'region' ,
label : 'Region' ,
getId : row => row . region
};
export function SalesCalendar ({ sales } : { sales : Sale [] }) {
return (
< PivotCalendar < Sale >
mode = "local"
data = { sales }
getDate = { row => row . date }
groupBy = { groupBy }
measures = { measures }
onCellClick = { info => console . log ( info . bucket , info . groupId , info . value ) } />
);
}
For a Docyrus data source, use useDocyrusPivotCalendar and spread its pivotCalendarProps.
Prop Type Default Description measuresIPivotCalendarMeasure<TData>[]— Required. Measures to aggregate and display.mode'local' | 'remote''local''local' aggregates data; 'remote' reads pre-aggregated cells.dataTData[]— Raw rows. Required when mode='local'. cellsIPivotCalendarRemoteCell[]— Pre-aggregated cells. Required when mode='remote'. getDate(row: TData) => Date | string— Reads a row's date. Required when mode='local'. groupByIPivotCalendarGroupBy<TData>— Optional grouping dimension. Enables the group filter sheet and the grid rows. groupsIPivotCalendarGroup[]inferred Group catalog. Required for mode='remote' with groupBy; inferred from data in local mode. defaultViewTPivotCalendarView'month-calendar'Initial view. viewTPivotCalendarView— Controlled view. onViewChange(view: TPivotCalendarView) => void— Called when the view changes. defaultDateDatenew Date()Initial reference date. dateDate— Controlled reference date. onDateChange(date: Date) => void— Called when the reference date changes. defaultSelectedGroupIdsstring[]all groups Initially selected groups. selectedGroupIdsstring[]— Controlled group selection. onSelectedGroupIdsChange(ids: string[]) => void— Called when the group selection changes. hideViewSwitcherbooleanfalseHide the view switcher. hideSidebarbooleanfalseHide the group filter button and sheet (the web side panel). visibleViewsTPivotCalendarView[]all 4 views Views exposed in the switcher. titleReactNoderange label Custom toolbar title. onCellClick(info: IPivotCalendarCellClick<TData>) => void— Fires when a measure value is tapped: a day-sheet row or a grid value cell. Use it for drilldown. maxCellMeasuresnumber2Native only. How many measures the month cells (value + dots) and the grid views render. The day sheet always lists every measure.size'default' | 'sm' | 'lg''default'Day-cell height (52 / 44 / 64 pt) and grid-row height (44 / 36 / 48 pt). On web this sets the container's min-h. classNamestring— Container classes.
Field Type Description idstringStable id. labelstringVisible label. shortLabelstringCompact label for the legend and the grid headers. aggregate'sum' | 'count' | 'avg' | 'min' | 'max'Aggregation (local mode). getValue(row: TData) => number | null | undefinedValue reader (local mode). count without getValue counts rows. formatValue(value: number) => stringDisplay formatter. colorstringAccent for dots and legend (hex or Tailwind family-shade). descriptionstringShown in the day sheet.
Field Type Description groupBy.idstringDimension id. groupBy.labelstringLabel for the filter sheet and the grid's group column. groupBy.getId(row: TData) => string | null | undefinedGroup id of a row. groupBy.getGroup(row: TData) => IPivotCalendarGroup | null | undefinedOptional group descriptor (label, avatar). group.id / group.labelstringIdentity and label. group.color / group.icon / group.imagestring | null / string | null / { signed_url?, file_name? } | nullRendered with AvatarThumbnail. group.descriptionstringOptional description.
Field Type Description bucketstringyyyy-MM-dd (day) or yyyy-MM (months-of-year).bucketStart / bucketEndDateInclusive local-time range of the bucket. viewTPivotCalendarViewActive view. groupIdstring | nullGroup of the tapped value. Month totals carry a group only when exactly one group is selected. groupIPivotCalendarGroupGroup entry, when available. measureIPivotCalendarMeasure<TData>Tapped measure. value / formattedValuenumber / stringRaw and formatted value.
Field Type Description bucketstringYYYY-MM-DD for days, YYYY-MM for months.groupIdstring | nullGroup id, or omitted for ungrouped totals. values{ measureId: string; value: number }[]Aggregated values.
Export Description usePivotCalendarController(props)Headless controller (IPivotCalendarController): view / date / group state, getCellValues, formatValue, navigation, rangeLabel, emitCellClick. bucketKeyForDate(date, view)Bucket key for a date in a view. buildLocalCellMap({ data, getDate, view, measures, groupBy })Aggregates raw rows into a bucket::groupId map. buildRemoteCellMap(cells)Indexes remote cells the same way. getRangeForView(date, view) / getRangeForBucket(bucket, view)Date range of a view or a bucket. navigateDate(date, view, 'previous' | 'next')Steps a week, a month or a year. rangeLabelForView(date, view)Toolbar title (May 2026, May 3 – 9, 2026, 2026). pivotCalendarVariantstv() slots base / dayCell / gridRow with the size variant.
Component Description PivotCalendarToolbar, active view, group filter sheet and day sheet.
Type Description PivotCalendarProps<TData>Component props (without size). PivotCalendarRootProps<TData>PivotCalendarProps + size variant.TPivotCalendarView'month-calendar' | 'days-of-week' | 'days-of-month' | 'months-of-year'TPivotCalendarMode'local' | 'remote'TPivotCalendarAggregate'sum' | 'count' | 'avg' | 'min' | 'max'TPivotCalendarBucketKind'day' | 'weekday' | 'month'IPivotCalendarMeasure<TData>Measure descriptor. IPivotCalendarGroupGroup descriptor. IPivotCalendarGroupBy<TData>Grouping dimension. IPivotCalendarMeasureValue{ measureId, value }.IPivotCalendarCellClick<TData>onCellClick payload.IPivotCalendarRemoteCellPre-aggregated cell. IPivotCalendarController<TData>Return type of usePivotCalendarController.
Copy uses useUiTranslation() with English fallbacks: ui.pivotCalendar.today, previousPeriod, nextPeriod, filterGroups, views, viewMonth, viewWeek, viewDays, viewYear, groups, all, clear, noGroups, totals, total, byGroup (By {{group}}), tapToDrillDown, and weekdaySun … weekdaySat.