Start hereApplies toSuperScheduler Lite
Quick start with Lite
Run npm install super-scheduler-lite, import SuperSchedulerComponent and super-scheduler-lite/styles.css, and pass startDate, days, resources and events. Lite renders a read-only, virtualized timeline with one cell per day, reports clicks through onEventClick and onTimeRangeClick, and throws an error for any option it does not implement.
SuperScheduler Lite is the public, read-only edition: one row per resource, one column per day, events as bars, clicks reported to your code. It is the fastest way to put an occupancy or availability chart in a React application. This guide takes you from an empty project to a working timeline, then covers every option, the callbacks, the imperative API and what Lite deliberately rejects.
If you need dragging, resizing, hours and minutes, zoom or resource trees, those belong to Pro: see Install SuperScheduler Pro and Migrate from Lite to Pro.
Requirements
- React 18.2 or later, or React 19. React is a peer dependency, so Lite uses your application's copy.
- A bundler or framework that understands ES modules or CommonJS (Vite, Next.js, webpack, Parcel and similar). Both formats and their TypeScript declarations ship in the package.
- A browser environment to render. The package can be imported during server rendering; the timeline itself is built in the browser when the component mounts.
Install the package
npm install super-scheduler-lite react react-domreact-dom is listed because you render with it, not because Lite imports it.
Render a first timeline
import { SuperSchedulerComponent } from 'super-scheduler-lite'
import type { SuperScheduler } from 'super-scheduler-lite'
import 'super-scheduler-lite/styles.css'
// Module-level arrays keep the same identity on every render, so React never re-applies them.
const ROOMS: SuperScheduler.ResourceData[] = [
{ id: 'r101', name: 'Room 101' },
{ id: 'r102', name: 'Room 102' },
{ id: 'r103', name: 'Room 103' },
]
const BOOKINGS: SuperScheduler.EventData[] = [
// Date-only values: the bar covers 2, 3 and 4 October (the end is exclusive).
{ id: 1, resource: 'r101', start: '2026-10-02', end: '2026-10-05', text: 'Booking 1042' },
// Overlaps the first booking on the same row: Lite stacks it on a second line.
{
id: 2,
resource: 'r101',
start: '2026-10-04',
end: '2026-10-07',
text: 'Booking 1043',
backColor: '#dbeafe',
},
// Times are kept: the bar starts at 14:00 and ends at 11:00, inside the day cells.
{
id: 3,
resource: 'r103',
start: '2026-10-06T14:00:00',
end: '2026-10-09T11:00:00',
text: 'Booking 1051',
},
]
export function Planning() {
return (
<SuperSchedulerComponent startDate="2026-10-01" days={31} resources={ROOMS} events={BOOKINGS} />
)
}You should see a 400-pixel-tall grid with a header of day labels (1 Oct, 2 Oct, …), three room rows and three bars. Booking 1042 covers 2, 3 and 4 October: the end date is exclusive, so a stay that ends on 2026-10-05 is gone at midnight of the 5th. Booking 1043 overlaps it, so Room 101 grows to two lines and stacks both bars. Booking 1051 starts at 14:00 on the 6th and ends at 11:00 on the 9th: Lite places bars at their exact times inside the day cells.
Scroll the grid in any direction. Only the rows, days and events in view exist in the DOM, and scrolling never causes a React render, whatever the size of your data.
Import the styles
Import super-scheduler-lite/styles.css once, typically in your entry file or root layout. The rules live in a CSS cascade layer named super-scheduler, so any unlayered rule in your own stylesheet overrides them without !important.
The root element has the class super-scheduler-lite and six custom properties. Override them on that class (not on a distant ancestor, because the root declares its own values):
.super-scheduler-lite {
--super-scheduler-background: #ffffff;
--super-scheduler-text: #18212f;
--super-scheduler-border: #dce3ed;
--super-scheduler-header: #f4f7fb;
--super-scheduler-event: #d7e8fa;
--super-scheduler-focus: #005cbf;
}
/* A dark theme driven by your own class on <html>. */
.dark .super-scheduler-lite {
--super-scheduler-background: #121518;
--super-scheduler-text: #f4f4f5;
--super-scheduler-border: #2b3139;
--super-scheduler-header: #1b1f24;
--super-scheduler-event: #1f3a5c;
}Lite sets its own font (13 px system UI) and fills the width of its parent. Per-event colors come from the data (backColor, fontColor) or from a cssClass you style yourself.
Options and defaults
Every option Lite accepts is in this table. Anything else throws (see What Lite rejects).
| Option | Type | Default | Notes |
|---|---|---|---|
startDate | ISO string or SuperScheduler.Date | Today | The first day; a time of day is ignored |
days | positive integer | 31 | Number of day columns |
scale | 'Day' | 'Day' | The only accepted value |
cellWidth | number (px) | 64 | Width of one day |
height | number (px) | 400 | Total height of the scrolling box, header included |
rowHeaderWidth | number (px) | 160 | Width of the resource name column |
rowMinHeight | number (px) | 40 | Rows grow when overlapping events stack |
eventHeight | number (px) | 26 | Height of one event line |
resources | ResourceData[] | [] | { id, name }, flat |
events | EventData[] | [] | See Event fields |
locale | string | 'en-us' | Day labels in the header, such as es-es or de-de |
ariaLabel | string | 'Resource schedule' | Accessible name of the grid, also shown in the top-left corner |
emptyState | string | 'No resources' | Text shown when resources is empty |
onEventClick | function | none | See Respond to clicks |
onTimeRangeClick | function | none | See Respond to clicks |
Numeric options must be positive and finite, and days must be an integer.
Event and resource fields
A resource is { id, name }. An event has five required fields and five optional ones:
| Field | Required | Meaning |
|---|---|---|
id | yes | String or finite number, unique among events |
resource | yes | The id of the row it belongs to, with the same type |
start, end | yes | ISO strings (2026-10-02 or 2026-10-02T14:00:00, seconds included) or SuperScheduler.Date; end is exclusive |
text | yes | The label, rendered as text (never as HTML) |
backColor, fontColor | no | Any CSS color |
cssClass | no | Extra class names on the event button |
toolTip | no | Native tooltip; defaults to text |
tags | no | Any value you want back in onEventClick |
Ids are compared strictly: 1 and '1' are different ids, so an event with resource: '101' does not appear on a row with id: 101. Dates are civil wall-clock values with no time zone; the data model guide explains the rules, which are the same in both editions.
Respond to clicks
Lite reports two interactions. onEventClick receives { control, e, originalEvent }, where e.data is your event object. onTimeRangeClick receives { control, start, end, resource, originalEvent } for a click on an empty day cell; start is that day at midnight and end the next midnight, both as SuperScheduler.Date.
import { useCallback, useState } from 'react'
import { SuperSchedulerComponent } from 'super-scheduler-lite'
import type {
SchedulerEventClickArgs,
SchedulerTimeRangeClickArgs,
SuperScheduler,
} from 'super-scheduler-lite'
interface PlanningProps {
readonly rooms: SuperScheduler.ResourceData[]
readonly bookings: SuperScheduler.EventData[]
}
export function PlanningWithDetails({ rooms, bookings }: PlanningProps) {
const [detail, setDetail] = useState('Select a booking or a free day.')
// Stable callbacks: a new function per render would be sent to the control on every render.
const onEventClick = useCallback((args: SchedulerEventClickArgs) => {
// Lite hands you the event's own data object, including `tags`.
setDetail(`${args.e.data.text} (id ${String(args.e.data.id)})`)
}, [])
const onTimeRangeClick = useCallback((args: SchedulerTimeRangeClickArgs) => {
// One day cell: `end` is the next midnight. Enter and Space on the active cell also land here.
setDetail(`Free cell: ${String(args.resource)} on ${args.start.toString('d MMMM yyyy')}`)
}, [])
return (
<>
<p aria-live="polite">{detail}</p>
<SuperSchedulerComponent
startDate="2026-10-01"
days={31}
resources={rooms}
events={bookings}
onEventClick={onEventClick}
onTimeRangeClick={onTimeRangeClick}
/>
</>
)
}You should see the paragraph change when you click a booking or a free cell. The same callbacks run from the keyboard: Tab focuses the grid, the arrow keys move the active cell, and Enter or Space on it calls onTimeRangeClick; events are buttons, so Enter on a focused event calls onEventClick. originalEvent is the DOM event behind the call: the KeyboardEvent when Enter or Space activated a cell, a click event otherwise.
Control the timeline from code
The React component creates a control when it mounts and disposes it when it unmounts. Reach it through ref.current.control on the component, or with the controlRef prop (a ref object or a callback; Lite sets it to null on unmount).
import { useRef } from 'react'
import { SuperScheduler, SuperSchedulerComponent } from 'super-scheduler-lite'
interface PlanningProps {
readonly rooms: SuperScheduler.ResourceData[]
readonly bookings: SuperScheduler.EventData[]
}
export function NavigablePlanning({ rooms, bookings }: PlanningProps) {
// Lite sets `current` after mount and clears it on unmount.
const controlRef = useRef<SuperScheduler.Scheduler | null>(null)
const goToToday = () => controlRef.current?.scrollTo(SuperScheduler.Date.today())
const findRoom = (id: SuperScheduler.ResourceData['id']) =>
controlRef.current?.scrollToResource(id)
const logRange = () => {
const control = controlRef.current
if (control !== null)
console.info(`${control.visibleStart().value} to ${control.visibleEnd().value}`)
}
return (
<>
<div role="toolbar" aria-label="Planning navigation">
<button type="button" onClick={goToToday}>
Today
</button>
<button type="button" onClick={() => findRoom('r310')}>
Room 310
</button>
<button type="button" onClick={logRange}>
Visible range
</button>
</div>
<SuperSchedulerComponent
controlRef={controlRef}
startDate="2026-10-01"
days={92}
height={520}
resources={rooms}
events={bookings}
/>
</>
)
}The Lite control has eight members:
| Member | What it does |
|---|---|
update(options) | Merges options into the current ones and redraws. An explicit undefined restores a default |
scrollTo(date) | Scrolls so date is at the left edge |
scrollToResource(id) | Scrolls so that row is at the top |
visibleStart(), visibleEnd() | The dates at the left and right edges of the scrolled view |
disposed() | Whether dispose() has run |
dispose() | Removes the DOM, listeners and observers and releases the data |
init() | Builds the DOM; the React component calls it for you |
Without React, create the control on an element you own:
import { SuperScheduler } from 'super-scheduler-lite'
import 'super-scheduler-lite/styles.css'
/** Mounts a read-only planning into `host` without React and returns its teardown. */
export function mountPlanning(host: HTMLElement): () => void {
const control = new SuperScheduler.Scheduler(host, {
startDate: '2026-10-01',
days: 31,
resources: [
{ id: 'r101', name: 'Room 101' },
{ id: 'r102', name: 'Room 102' },
],
events: [
{ id: 1, resource: 'r101', start: '2026-10-02', end: '2026-10-05', text: 'Booking 1042' },
],
onEventClick: ({ e }) => console.info('booking', e.data.id),
})
control.init()
// update() merges with the current options; an explicit undefined restores a default.
control.update({ days: 62, cellWidth: 48 })
control.scrollTo('2026-10-15')
return () => control.dispose()
}Update the data
The component sends only changed props to control.update(), comparing them by identity. To change the data, pass a new array: setEvents([...events, next]) works, while events.push(next) on the same array does not reach the timeline until you call control.update() yourself. Updates that only change onEventClick or onTimeRangeClick swap the callbacks without redrawing.
What Lite rejects
Lite validates its input and throws instead of ignoring what it cannot do, so a misconfiguration surfaces during development rather than as a half-working screen:
- An option outside the table above, including Pro options such as
allowEventOverlaporzoomLevels, even when passed from plain JavaScript:SuperScheduler Lite: unsupported option "zoomLevels". scaleother than'Day'.- A resource with
children,frozen,splitorcolumns(resource children requires Pro). - Duplicate resource ids, ids that are not strings or finite numbers, and events whose
endis before theirstart. - Non-positive or non-finite sizes, and a fractional
days.
When update() throws, the previous configuration stays on screen and usable. In React the error is thrown while the component commits the new props, so an error boundary above it catches it.
Next steps
- Understand the data rules shared by both editions: Resources, events and intervals.
- Embed the component correctly in a larger React app: React integration.
- See what an editable planning looks like in the examples, all built with Pro.
- When you need editing: Migrate from Lite to Pro.