ObjectStackObjectStack
Custom Front-endUI plugins

Plugin Calendar

Calendar view components for ObjectUI - includes both ObjectQL-integrated and standalone calendar components.

Installation

npm install @object-ui/plugin-calendar

The props an object-calendar node reads on a page are generated from the spec into the component reference; this page covers using the plugin from React.

Overview

The @object-ui/plugin-calendar plugin provides two calendar components:

  1. ObjectCalendar (object-calendar): For ObjectQL data sources - displays database records as calendar events
  2. CalendarView (calendar-view): Standalone calendar component for displaying pre-loaded event data

Both components support month, week, and day views with full event management capabilities.

Every week starts on the first day of the week of the calendar's locale (the locale prop, else the user's display locale): Sunday under en-US, Monday under en-GB or zh-CN. The month grid, its weekday heads, the week view's columns, the header's week range and the date popover all start on that day. There is no separate week-start setting.

Drag-and-Drop Rescheduling

Month view

  • Move an event — grab any cell of the event pill and drop it on another day. The calendar days from the grabbed day to the drop day move both startDateField and endDateField, so dragging a multi-day span from any of its days behaves intuitively. Each value keeps its time of day: a 10:00 event stays at 10:00, even across a DST change.
  • Resize an event — grab the small right-edge handle on the last day of a multi-day pill and drop it on a different day to extend or shrink the end date. The end keeps its time of day, and the start date is preserved; drops earlier than the start are ignored.

Week / Day view (time grid)

Week and day views render a classic Google Calendar-style vertical time grid. All gestures use pointer events and snap to slotMinutes (default 30):

  • Move an event — drag the event body vertically to change its start time (and end, by the same delta). In week view, drag horizontally to also change the day.
  • Resize start — drag the top edge of an event to adjust only the start time. Refuses to cross the existing end.
  • Resize end — drag the bottom edge to adjust only the end time. Refuses to cross the existing start.
  • Drag-to-create — click-drag on an empty area of the time grid to open the quick-create dialog with start/end pre-filled to the dragged time range.

Pass slotMinutes={15} to change the snap granularity, or onTimeRangeSelect={(start, end) => …} to override the drag-to-create default.

When ObjectCalendar is bound to an object (i.e. it has objectName and a dataSource), the new dates are persisted automatically via dataSource.update() — local state is updated optimistically and rolled back on failure. To intercept (e.g. for a confirm dialog) pass an onEventDrop prop; supplying your own handler disables the default persistence.

The default persistence is offered only to a caller who may make the write: events are draggable when the calendarReschedule row of the affordance-to-grant map (@object-ui/core) allows it — the object's policy, the effective API operation set and the caller's update grant. Without it the events stay where they are. With no permission provider mounted it reads open.

<ObjectCalendar
  schema={{ type: 'object-calendar', objectName: 'campaign' }}
  // optional — omit to get default backend persistence
  onEventDrop={(record, newStart, newEnd) => {
    if (confirm(`Move ${record.name}?`)) save({ id: record.id, start_date: newStart, end_date: newEnd });
  }}
/>

Click-to-Create

Clicking the empty area of any day cell (month view) or click-dragging in the week/day time grid opens a quick-create dialog pre-filled with the selected date/range. Type a title and press Enter (or click Create) to persist a new record via dataSource.create(objectName, payload). The new record is inserted optimistically into the calendar so it appears immediately.

The payload includes the configured titleField, startDateField, optional endDateField (set to the clicked day), plus auto-defaults for any other required fields not provided by the user (first picklist option for select/status, false for booleans, 0 for numerics, or the field's declared defaultValue). This ensures the create succeeds against NOT NULL columns without forcing the user through the full form.

When the object has a required field outside that set (a required lookup, or a required text field other than the title), the dialog's create could not succeed, so it is not offered. The same gesture opens the host's full create form instead: the calendar calls onEdit with a new record (no id) carrying the date values the dialog would have written, and the host pre-fills its create form with them. The console does this already. Without an onEdit, the gesture offers nothing on such an object. Required fields the server fills on insert (readonly, system, autonumber) do not count.

<ObjectCalendar
  schema={{ type: 'object-calendar', objectName: 'task', calendar: { startDateField: 'due_date' } }}
  // Called as onEdit({ due_date: '2026-10-20' }) on an object with a required lookup.
  onEdit={(record) => openCreateForm({ initialValues: record })}
/>

Quick-create is offered only when the calendarQuickCreate row of the affordance-to-grant map allows it (the caller's create grant, with the object's policy and the effective API operation set): without it a day click opens nothing and the time grid starts no range drag. With no permission provider mounted it reads open.

To override (e.g. open your own multi-field create form), pass onDateClick:

<ObjectCalendar
  schema={{ type: 'object-calendar', objectName: 'campaign' }}
  onDateClick={(day) => navigate(`/campaign/new?start=${day.toISOString()}`)}
/>

CalendarView Component

Full-featured standalone calendar with month, week, and day views for displaying events and scheduling.

Example schemas

Month View

Month View Calendar

{
  "type": "calendar-view",
  "view": "month",
  "currentDate": "2026-05-15T12:00:00.000Z",
  "data": [
    {
      "id": 1,
      "title": "Team Meeting",
      "start": "2026-05-28T02:00:00.000Z",
      "end": "2026-05-28T03:00:00.000Z",
      "color": "#3b82f6",
      "allDay": false
    },
    {
      "id": 2,
      "title": "Project Deadline",
      "start": "2026-05-31T06:32:37.758Z",
      "color": "#ef4444",
      "allDay": true
    },
    {
      "id": 3,
      "title": "Conference",
      "start": "2026-06-04T06:32:37.758Z",
      "end": "2026-06-06T06:32:37.758Z",
      "color": "#10b981",
      "allDay": true
    }
  ],
  "titleField": "title",
  "startDateField": "start",
  "endDateField": "end",
  "colorField": "color",
  "allDayField": "allDay",
  "className": "h-[600px] border rounded-lg"
}

Week View

Week View Calendar

{
  "type": "calendar-view",
  "view": "week",
  "currentDate": "2026-05-27T12:00:00.000Z",
  "data": [
    {
      "id": 1,
      "title": "Standup",
      "start": "2026-05-28T01:00:00.000Z",
      "end": "2026-05-28T01:30:00.000Z",
      "color": "#3b82f6",
      "allDay": false
    },
    {
      "id": 2,
      "title": "Sprint Planning",
      "start": "2026-05-28T06:00:00.000Z",
      "end": "2026-05-28T07:30:00.000Z",
      "color": "#8b5cf6",
      "allDay": false
    },
    {
      "id": 3,
      "title": "1:1 with Manager",
      "start": "2026-05-29T03:00:00.000Z",
      "end": "2026-05-29T03:30:00.000Z",
      "color": "#10b981",
      "allDay": false
    },
    {
      "id": 4,
      "title": "Team Lunch",
      "start": "2026-05-30T04:00:00.000Z",
      "end": "2026-05-30T05:00:00.000Z",
      "color": "#f59e0b",
      "allDay": false
    }
  ],
  "titleField": "title",
  "startDateField": "start",
  "endDateField": "end",
  "colorField": "color",
  "allDayField": "allDay",
  "className": "h-[500px] border rounded-lg"
}

CalendarView Usage

import '@object-ui/plugin-calendar'
import type { CalendarViewSchema } from '@object-ui/types'

const schema: CalendarViewSchema = {
  type: 'calendar-view',
  view: 'month',
  data: [
    {
      id: 1,
      title: 'Team Meeting',
      start: '2024-01-15T10:00:00',
      end: '2024-01-15T11:00:00',
      color: '#3b82f6'
    }
  ]
}

CalendarView Schema API

{
  type: 'calendar-view',
  view?: 'month' | 'week' | 'day',
  data?: Array<CalendarEventData>,
  titleField?: string,
  startDateField?: string,
  endDateField?: string,
  allDayField?: string,
  colorField?: string,
  currentDate?: string,
  allowCreate?: boolean,
  onEventClick?: (event: any) => void,
  onDateClick?: (date: Date) => void,
  onViewChange?: (view: string) => void,
  onNavigate?: (date: Date) => void,
  className?: string
}

Allowing event creation

allowCreate: true adds the header's New event button. Clicking it dispatches the standard create action — { type: 'create', payload: {} } — on the same action channel every other calendar-view gesture uses, so the host decides what a "create" means (open a form, navigate, call an API).

const schema = {
  type: 'calendar-view',
  data: events,
  allowCreate: true,
}

Only the boolean true turns the button on. Omitting the key, false, and any non-boolean value all render the calendar without the button — the default.

A React host can supply the affordance directly instead, with or without allowCreate, by passing its own onAddClick handler; an explicit handler replaces the action dispatch rather than running alongside it.

ObjectCalendar Component

Calendar component designed for use with ObjectQL data sources.

Features

  • ObjectQL Integration: Works seamlessly with object/value data providers
  • Automatic Field Mapping: Maps database fields to calendar events
  • Multiple View Modes: Month, week, and day calendar views
  • Visible days for a host: Reports the days it draws, so a host that fetches its records can fetch only those days (see Visible days); on its own it fetches the whole filtered set, up to the platform row ceiling
  • Event Interaction: Click handling for events and dates
  • Color Coding: Support for event color customization

ObjectCalendar Usage

Basic Usage with ObjectQL

import '@object-ui/plugin-calendar'
import type { ObjectCalendarSchema } from '@object-ui/types'

const schema: ObjectCalendarSchema = {
  type: 'object-calendar',
  objectName: 'events',  // Your ObjectQL object
  calendar: {
    startDateField: 'startDate',
    endDateField: 'endDate',
    titleField: 'title',
    colorField: 'category'
  }
}

With Static Data

import type { ObjectCalendarSchema } from '@object-ui/types'

const schema: ObjectCalendarSchema = {
  type: 'object-calendar',
  staticData: [
    {
      id: 1,
      title: 'Team Meeting',
      startDate: '2024-01-15T10:00:00',
      endDate: '2024-01-15T11:00:00',
      category: 'meeting'
    },
    {
      id: 2,
      title: 'Project Deadline',
      startDate: '2024-01-20',
      category: 'deadline'
    }
  ],
  calendar: {
    startDateField: 'startDate',
    endDateField: 'endDate',
    titleField: 'title',
    colorField: 'category'
  }
}

Comparison: CalendarView vs ObjectCalendar

FeatureCalendarViewObjectCalendar
Schema Typecalendar-viewobject-calendar
Data SourceStatic arraysObjectQL (database)
Use CasePre-loaded event dataDynamic data from backend
Field MappingStandard field namesConfigurable field names
Data LoadingManual via propsAutomatic via ObjectQL
Best ForStatic schedulesDatabase-driven apps

When to use CalendarView:

  • You have static or pre-loaded event data
  • You're not using ObjectQL
  • You need a simple, standalone calendar

When to use ObjectCalendar:

  • You're using ObjectQL for data management
  • Events come from a database or API
  • You need automatic data fetching and filtering

Event Handling

A click handler is a function, so it is a prop of the ObjectCalendar component a React host renders, never a key on the node: ObjectCalendarSchema declares no onEventClick or onDateClick, and authored JSON can never carry a function anyway.

Event Click

import { ObjectCalendar } from '@object-ui/plugin-calendar';
import type { DataSource, ObjectCalendarSchema } from '@object-ui/types';

const eventClickCalendar: ObjectCalendarSchema = {
  type: 'object-calendar',
  objectName: 'events',
  calendar: {
    startDateField: 'start',
    titleField: 'title'
  }
};

export function EventClickCalendar({ dataSource }: { dataSource: DataSource }) {
  return (
    <ObjectCalendar
      schema={eventClickCalendar}
      dataSource={dataSource}
      onEventClick={(record) => {
        console.log('Event clicked:', record);
        // Open event details, or navigate to the event page
      }}
    />
  );
}

Date Click

import { ObjectCalendar } from '@object-ui/plugin-calendar';
import type { DataSource, ObjectCalendarSchema } from '@object-ui/types';

const dateClickCalendar: ObjectCalendarSchema = {
  type: 'object-calendar',
  objectName: 'events',
  calendar: {
    startDateField: 'start',
    titleField: 'title'
  }
};

export function DateClickCalendar({ dataSource }: { dataSource: DataSource }) {
  return (
    <ObjectCalendar
      schema={dateClickCalendar}
      dataSource={dataSource}
      onDateClick={(date) => {
        console.log('Date clicked:', date);
        // Create a new event on this date
      }}
    />
  );
}

Visible days

A host that fetches the calendar's records for it, and hands them over as data, can fetch only the days on screen. onVisibleRangeChange receives { start, end } once on mount and again whenever navigation or a view change moves them: start is local midnight of the first day drawn and end local midnight of the day after the last. In the month view the range covers the whole six-week grid, including the neighbouring months' leading and trailing days, under the locale's first day of the week; in the week view it is the week, and in the day view the day. An unchanged range is not reported again. A list view's calendar uses it to fetch the days it shows instead of one batch of the object (objectui#12081).

Direct Component Usage

You can also import and use the components directly in React:

import { CalendarView, ObjectCalendar } from '@object-ui/plugin-calendar';
import type { DataSource, ObjectCalendarSchema } from '@object-ui/types';

// CalendarView - Standalone calendar
function MyCalendar() {
  const events = [
    {
      id: 1,
      title: 'Meeting',
      start: new Date('2024-01-15T10:00:00'),
      end: new Date('2024-01-15T11:00:00'),
      color: '#3b82f6'
    }
  ];

  return (
    <CalendarView
      events={events}
      view="month"
      onEventClick={(event) => console.log(event)}
    />
  );
}

// ObjectCalendar - ObjectQL-integrated
function MyObjectCalendar({ dataSource }: { dataSource: DataSource }) {
  const schema: ObjectCalendarSchema = {
    type: 'object-calendar',
    objectName: 'events',
    calendar: {
      startDateField: 'startDate',
      titleField: 'title'
    }
  };

  return (
    <ObjectCalendar
      schema={schema}
      dataSource={dataSource}
      onEventClick={(record) => console.log(record)}
    />
  );
}

TypeScript Support

import type { 
  CalendarViewSchema, 
  ObjectCalendarSchema,
  CalendarConfig 
} from '@object-ui/types'
import type { CalendarViewEvent } from '@object-ui/plugin-calendar'

// CalendarView component events — the RUNTIME shape: `id: string | number`,
// `start` / `end` are real `Date` objects. `@object-ui/types` exports a
// separate `CalendarEvent`, the AUTHORING event (`id: string`, ISO strings,
// `end` required); the two are not interchangeable. Renamed in objectui#5044,
// which left `CalendarEvent` on this package as a `@deprecated` alias.
const events: CalendarViewEvent[] = [
  {
    id: 1,
    title: 'Meeting',
    start: new Date('2024-01-15T10:00:00'),
    end: new Date('2024-01-15T11:00:00'),
    color: '#3b82f6'
  }
]

const calendarViewSchema: CalendarViewSchema = {
  type: 'calendar-view',
  view: 'month',
  data: events
}

// ObjectCalendar types
const calendarConfig: CalendarConfig = {
  startDateField: 'startDate',
  endDateField: 'endDate',
  titleField: 'title',
  colorField: 'category'
}

const objectCalendarSchema: ObjectCalendarSchema = {
  type: 'object-calendar',
  objectName: 'events',
  calendar: calendarConfig
}

Migration from @object-ui/plugin-calendar-view

The @object-ui/plugin-calendar-view package has been merged into this package. If you were using it:

Before

npm install @object-ui/plugin-calendar-view
import '@object-ui/plugin-calendar-view'
import { CalendarView } from '@object-ui/plugin-calendar-view'

After

npm install @object-ui/plugin-calendar
import '@object-ui/plugin-calendar'
import { CalendarView } from '@object-ui/plugin-calendar'

All functionality remains the same - just update your imports and package dependencies.

On this page