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).

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

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