Skip to content

Calendar ​

A full-featured native calendar view for NativeScript with month, week, and year display modes, multiple selection modes, event markers, custom styling, and programmatic control.

The plugin celebrates two best-in-class native libraries:

Installation ​

bash
npm install @nstudio/nativescript-calendar

Register the Component ​

typescript
import { registerElement } from '@nativescript/angular';
import { NCalendar } from '@nstudio/nativescript-calendar';

registerElement('NCalendar', () => NCalendar);
typescript
import { registerElement } from 'react-nativescript';
import { NCalendar } from '@nstudio/nativescript-calendar';

registerElement('nCalendar', () => NCalendar);
typescript
import { registerElement } from 'nativescript-vue';
import { NCalendar } from '@nstudio/nativescript-calendar';

registerElement('NCalendar', () => NCalendar);
typescript
import { registerNativeViewElement } from '@nativescript-community/svelte-native/dom';
import { NCalendar } from '@nstudio/nativescript-calendar';

registerNativeViewElement('nCalendar', () => NCalendar);
typescript
import { registerElement } from 'dominative';
import { NCalendar } from '@nstudio/nativescript-calendar';

registerElement('nCalendar', NCalendar);

NativeScript Core does not require registration — use the XML namespace:

xml
<Page xmlns:cal="@nstudio/nativescript-calendar">
  ...
</Page>

Basic Usage ​

html
<NCalendar
  displayMode="month"
  selectionMode="single"
  orientation="vertical"
  interMonthSpacing="16"
  (daySelect)="onDaySelect($event)">
</NCalendar>
typescript
import { CalendarDayEventData } from '@nstudio/nativescript-calendar';

export class CalendarComponent {
  onDaySelect(args: CalendarDayEventData) {
    const day = args.data.day;
    console.log(`Selected: ${day.year}-${day.month}-${day.day}`);
  }
}
tsx
<nCalendar
  displayMode="month"
  selectionMode="single"
  orientation="vertical"
  interMonthSpacing={16}
  onDaySelect={handleDaySelect}
  className="h-full"
/>
typescript
import { CalendarDayEventData } from '@nstudio/nativescript-calendar';

function handleDaySelect(args: CalendarDayEventData) {
  const day = args.data.day;
  console.log(`Selected: ${day.year}-${day.month}-${day.day}`);
}
vue
<NCalendar
  displayMode="month"
  selectionMode="single"
  orientation="vertical"
  :interMonthSpacing="16"
  @daySelect="onDaySelect"
/>
typescript
import { CalendarDayEventData } from '@nstudio/nativescript-calendar';

function onDaySelect(args: CalendarDayEventData) {
  const day = args.data.day;
  console.log(`Selected: ${day.year}-${day.month}-${day.day}`);
}
svelte
<nCalendar
  displayMode="month"
  selectionMode="single"
  orientation="vertical"
  interMonthSpacing={16}
  on:daySelect={handleDaySelect}
/>
typescript
import { CalendarDayEventData } from '@nstudio/nativescript-calendar';

function handleDaySelect(args: CalendarDayEventData) {
  const day = args.data.day;
  console.log(`Selected: ${day.year}-${day.month}-${day.day}`);
}
jsx
<nCalendar
  displayMode="month"
  selectionMode="single"
  orientation="vertical"
  interMonthSpacing={16}
  onDaySelect={handleDaySelect}
/>
typescript
import { CalendarDayEventData } from '@nstudio/nativescript-calendar';

function handleDaySelect(args: CalendarDayEventData) {
  const day = args.data.day;
  console.log(`Selected: ${day.year}-${day.month}-${day.day}`);
}

Display Modes ​

Month (default) ​

The standard month view with continuous vertical or horizontal scrolling.

html
<NCalendar displayMode="month"></NCalendar>
tsx
<nCalendar displayMode="month" />
vue
<NCalendar displayMode="month" />
svelte
<nCalendar displayMode="month" />
jsx
<nCalendar displayMode="month" />

Week ​

A single-row week strip. Swipe left or right to navigate between weeks.

html
<NCalendar displayMode="week"></NCalendar>
tsx
<nCalendar displayMode="week" />
vue
<NCalendar displayMode="week" />
svelte
<nCalendar displayMode="week" />
jsx
<nCalendar displayMode="week" />

Year ​

A grid of mini-month calendars. Control the number of columns with monthColumns.

html
<NCalendar displayMode="year" [monthColumns]="3"></NCalendar>
tsx
<nCalendar displayMode="year" monthColumns={3} />
vue
<NCalendar displayMode="year" :monthColumns="3" />
svelte
<nCalendar displayMode="year" monthColumns={3} />
jsx
<nCalendar displayMode="year" monthColumns={3} />

Selection Modes ​

Single (default) ​

Tap to select one date. Tap again to deselect.

Range ​

Tap a start date, then tap an end date. Dates in between are highlighted with a range band.

html
<NCalendar selectionMode="range" (daySelect)="onRangeSelect($event)"></NCalendar>
typescript
onRangeSelect(args: CalendarDayEventData) {
  const calendar = args.object as NCalendar;
  const range = calendar.selectedDateRange;
  if (range) {
    console.log(`${range.start} to ${range.end}`);
  }
}
tsx
<nCalendar
  selectionMode="range"
  onDaySelect={handleRangeSelect}
/>
typescript
function handleRangeSelect(args: CalendarDayEventData) {
  const calendar = args.object as NCalendar;
  const range = calendar.selectedDateRange;
  if (range) {
    console.log(`${range.start} to ${range.end}`);
  }
}
vue
<NCalendar selectionMode="range" @daySelect="onRangeSelect" />
typescript
function onRangeSelect(args: CalendarDayEventData) {
  const calendar = args.object as NCalendar;
  const range = calendar.selectedDateRange;
  if (range) {
    console.log(`${range.start} to ${range.end}`);
  }
}
svelte
<nCalendar selectionMode="range" on:daySelect={handleRangeSelect} />
typescript
function handleRangeSelect(args: CalendarDayEventData) {
  const calendar = args.object as NCalendar;
  const range = calendar.selectedDateRange;
  if (range) {
    console.log(`${range.start} to ${range.end}`);
  }
}
jsx
<nCalendar selectionMode="range" onDaySelect={handleRangeSelect} />
typescript
function handleRangeSelect(args: CalendarDayEventData) {
  const calendar = args.object as NCalendar;
  const range = calendar.selectedDateRange;
  if (range) {
    console.log(`${range.start} to ${range.end}`);
  }
}

Multiple ​

Tap to toggle individual dates on or off.

typescript
const selected = calendar.getSelectedDates();
console.log(`${selected.length} dates selected`);

None ​

Day taps are disabled. Useful for display-only calendars.

Orientation & Paging ​

Vertical Scroll (default) ​

Months flow top to bottom in a continuous scroll.

Horizontal Paged ​

One month at a time. Swipe left or right to navigate.

html
<NCalendar orientation="horizontal" [scrollPaged]="true"></NCalendar>
tsx
<nCalendar orientation="horizontal" scrollPaged={true} />
vue
<NCalendar orientation="horizontal" :scrollPaged="true" />
svelte
<nCalendar orientation="horizontal" scrollPaged={true} />
jsx
<nCalendar orientation="horizontal" scrollPaged={true} />

Events / Markers ​

Add colored dot markers to calendar days using the events property.

typescript
import { CalendarEvent } from '@nstudio/nativescript-calendar';

const events: CalendarEvent[] = [
  { date: new Date(2026, 2, 3), color: '#E91E63', data: { title: 'Team Standup', time: '9:00 AM' } },
  { date: new Date(2026, 2, 3), color: '#2196F3', data: { title: 'Design Review', time: '2:00 PM' } },
  { date: new Date(2026, 2, 7), color: '#4CAF50', data: { title: 'Lunch with Sarah', time: '12:30 PM' } },
];
html
<NCalendar [events]="events" (daySelect)="onEventDaySelect($event)"></NCalendar>
tsx
<nCalendar events={events} onDaySelect={handleEventDaySelect} />
vue
<NCalendar :events="events" @daySelect="onEventDaySelect" />
svelte
<nCalendar events={events} on:daySelect={handleEventDaySelect} />
jsx
<nCalendar events={events()} onDaySelect={handleEventDaySelect} />

Each event produces a small colored dot below the day number. Multiple events on the same day show multiple dots. The data field is passed through for your use (e.g. to display event details on tap).

Disabled Dates ​

Prevent specific dates or entire weekdays from being selected. Disabled days are rendered with disabledDayTextColor and cannot be tapped.

Disable specific dates ​

typescript
// Disable holidays or other specific dates
calendar.disabledDates = [
  new Date(2026, 0, 1),   // New Year's Day
  new Date(2026, 11, 25), // Christmas
  new Date(2026, 6, 4),   // Independence Day
];

Disable weekdays ​

typescript
// Disable weekends (0 = Sunday, 6 = Saturday)
calendar.disabledWeekdays = [0, 6];

Combine both ​

html
<NCalendar
  [disabledDates]="holidays"
  [disabledWeekdays]="[0, 6]"
  disabledDayTextColor="#CCCCCC">
</NCalendar>
typescript
export class BookingComponent {
  holidays = [
    new Date(2026, 0, 1),
    new Date(2026, 11, 25),
  ];
}
tsx
<nCalendar
  disabledDates={holidays}
  disabledWeekdays={[0, 6]}
  disabledDayTextColor="#CCCCCC"
/>
vue
<NCalendar
  :disabledDates="holidays"
  :disabledWeekdays="[0, 6]"
  disabledDayTextColor="#CCCCCC"
/>
svelte
<nCalendar
  disabledDates={holidays}
  disabledWeekdays={[0, 6]}
  disabledDayTextColor="#CCCCCC"
/>
jsx
<nCalendar
  disabledDates={holidays()}
  disabledWeekdays={[0, 6]}
  disabledDayTextColor="#CCCCCC"
/>

Disabled dates are also reported as isDisabled: true in the dayRender event, allowing further customization.

Custom Styling ​

All style properties can be set via template attributes or programmatically.

html
<NCalendar
  dayTextColor="#212121"
  todayTextColor="#FF6B6B"
  todayBackgroundColor="#2D2D2D"
  selectedDayTextColor="#FFFFFF"
  selectedDayBackgroundColor="#FF6B6B"
  selectedRangeColor="#FF6B6B40"
  weekendTextColor="#666666"
  monthHeaderTextColor="#FF6B6B"
  dayOfWeekTextColor="#999999">
</NCalendar>
tsx
<nCalendar
  dayTextColor="#212121"
  todayTextColor="#FF6B6B"
  todayBackgroundColor="#2D2D2D"
  selectedDayTextColor="#FFFFFF"
  selectedDayBackgroundColor="#FF6B6B"
  selectedRangeColor="#FF6B6B40"
  weekendTextColor="#666666"
  monthHeaderTextColor="#FF6B6B"
  dayOfWeekTextColor="#999999"
/>
vue
<NCalendar
  dayTextColor="#212121"
  todayTextColor="#FF6B6B"
  todayBackgroundColor="#2D2D2D"
  selectedDayTextColor="#FFFFFF"
  selectedDayBackgroundColor="#FF6B6B"
  selectedRangeColor="#FF6B6B40"
  weekendTextColor="#666666"
  monthHeaderTextColor="#FF6B6B"
  dayOfWeekTextColor="#999999"
/>
svelte
<nCalendar
  dayTextColor="#212121"
  todayTextColor="#FF6B6B"
  todayBackgroundColor="#2D2D2D"
  selectedDayTextColor="#FFFFFF"
  selectedDayBackgroundColor="#FF6B6B"
  selectedRangeColor="#FF6B6B40"
  weekendTextColor="#666666"
  monthHeaderTextColor="#FF6B6B"
  dayOfWeekTextColor="#999999"
/>
jsx
<nCalendar
  dayTextColor="#212121"
  todayTextColor="#FF6B6B"
  todayBackgroundColor="#2D2D2D"
  selectedDayTextColor="#FFFFFF"
  selectedDayBackgroundColor="#FF6B6B"
  selectedRangeColor="#FF6B6B40"
  weekendTextColor="#666666"
  monthHeaderTextColor="#FF6B6B"
  dayOfWeekTextColor="#999999"
/>

Programmatic Control ​

Scrolling ​

typescript
// Scroll to today
calendar.goToToday(true);

// Scroll to a specific month
calendar.scrollToMonth(2026, 6, true);

// Scroll to a specific date
calendar.scrollToDate(new Date(2026, 5, 15), true);

// Navigate month by month
calendar.goToNextMonth(true);
calendar.goToPreviousMonth(true);

Selection ​

typescript
// Select a date
calendar.selectDate(new Date(2026, 2, 15));

// Deselect a date
calendar.deselectDate(new Date(2026, 2, 15));

// Select a range
calendar.selectDateRange(new Date(2026, 2, 1), new Date(2026, 2, 14));

// Get current selection
const dates: Date[] = calendar.getSelectedDates();

// Clear all selections
calendar.clearSelection();

Refresh ​

Force a full rebuild of the calendar when needed:

typescript
calendar.refresh();

Properties ​

Layout & Mode ​

PropertyTypeDefaultDescription
displayMode'month' | 'week' | 'year''month'Calendar display mode
selectionMode'none' | 'single' | 'multiple' | 'range''single'How days are selected
orientation'vertical' | 'horizontal''vertical'Scroll direction
scrollPagedbooleanfalseSnap to month/week boundaries

Date Range ​

PropertyTypeDefaultDescription
minDateDate2 years agoStart of scrollable range
maxDateDate2 years aheadEnd of scrollable range
firstDayOfWeeknumber0 (Sun)First day of week (0=Sun, 1=Mon...6=Sat)

Selection ​

PropertyTypeDefaultDescription
selectedDatesDate[][]Currently selected dates (single/multiple)
selectedDateRangeDateRangeundefinedCurrently selected range (range mode)

Events / Markers ​

PropertyTypeDefaultDescription
eventsCalendarEvent[][]Dot markers on days

Disabled Dates ​

PropertyTypeDefaultDescription
disabledDatesDate[][]Specific dates that cannot be selected
disabledWeekdaysnumber[][]Weekdays to disable (0=Sun, 1=Mon...6=Sat)

Spacing ​

PropertyTypeDefaultDescription
interMonthSpacingnumber0DIP between months
verticalDayMarginnumber0DIP between day rows
horizontalDayMarginnumber0DIP between day columns

Layout Options ​

PropertyTypeDefaultDescription
pinDaysOfWeekToTopbooleantrueSticky day-of-week header row
outDateStyle'endOfRow' | 'endOfGrid''endOfRow'How trailing dates are shown
monthColumnsnumber3Columns in year view grid

Style ​

PropertyTypeDefault
dayTextColorColorsystem
dayFontSizenumber18
todayTextColorColoraccent
todayBackgroundColorColortransparent
selectedDayTextColorColorwhite
selectedDayBackgroundColorColoraccent
selectedRangeColorColorlight accent
weekendTextColorColorgray
disabledDayTextColorColorlight gray
outDateTextColorColorvery light gray
monthHeaderTextColorColorsystem
monthHeaderFontSizenumber20
dayOfWeekTextColorColorgray
dayOfWeekFontSizenumber14

Events ​

EventData TypeDescription
daySelectCalendarDayEventDataA day was selected
dayDeselectCalendarDayEventDataA day was deselected
dateRangeDragCalendarDateRangeEventDataDrag across days (range mode)
scrollCalendarScrollEventDataDuring scroll
scrollEndCalendarScrollEventDataScroll settled
monthChangedCalendarMonthEventDataVisible month changed
dayRenderCalendarDayRenderEventDataDay cell rendered (custom styling)

Event Data Shapes ​

typescript
interface CalendarDayEventData extends EventData {
  data: { day: CalendarDay };
}

interface CalendarMonthEventData extends EventData {
  data: { month: CalendarMonth };
}

interface CalendarDayRenderEventData extends EventData {
  data: {
    day: CalendarDay;
    view: any;       // native view reference
    isSelected: boolean;
    isInRange: boolean;
    isDisabled: boolean;
    events: CalendarEvent[];
  };
}

Data Types ​

typescript
interface CalendarDay {
  date: Date;
  day: number;       // 1-31
  month: number;     // 1-12
  year: number;
  position: DayPosition;
  isToday: boolean;
  isWeekend: boolean;
}

interface CalendarMonth {
  month: number;     // 1-12
  year: number;
}

interface DateRange {
  start: Date;
  end: Date;
}

interface CalendarEvent {
  date: Date;
  color?: string;
  data?: any;
}

Methods ​

MethodDescription
scrollToDate(date, animated?, position?)Scroll to a specific date
scrollToMonth(year, month, animated?)Scroll to a specific month
goToToday(animated?)Scroll to the current date
goToNextMonth(animated?)Scroll forward one month
goToPreviousMonth(animated?)Scroll back one month
selectDate(date)Programmatically select a date
deselectDate(date)Programmatically deselect a date
selectDateRange(start, end)Programmatically select a range
clearSelection()Clear all selections
getSelectedDates()Returns Date[] of selected dates
refresh()Force a full calendar rebuild

Platform Notes ​

iOS ​

  • Uses Airbnb's HorizonCalendar via CocoaPods
  • Vertical month view supports pinned day-of-week headers (pinDaysOfWeekToTop)
  • Week view is implemented as a constrained single-row CalendarView

Android ​

  • Uses kizitonwose Calendar via Gradle
  • Native CalendarView, WeekCalendarView, and YearCalendarView for each display mode
  • Range selection renders with Airbnb-style start/end circles and connecting band
  • Day-of-week header row is managed natively with proper locale support
  • Requires coreLibraryDesugaring for java.time APIs on older Android versions

License ​

Apache License Version 2.0