ProductionApplies toLite and Pro
Troubleshooting
Most problems come from a few causes: the stylesheet is not imported, the host has no height for `height="100%"`, the control is read before mount, or the view shows one day of today while the data lives elsewhere (`days` defaults to 1 and `startDate` to today). Check also that date strings include seconds, that event `resource` values match resource ids exactly, and that only one copy of React is installed. Each section below gives the symptom, the cause and the fix.
Find the symptom, check the cause, apply the fix. Sections apply to Pro and Lite unless they name one edition. If your problem is not here, the API reference lists every implemented option with its default, and its reserved APIs section lists what is typed but not implemented.
The scheduler renders without styles
Symptom. Rows and events appear, but without grid lines, colors or aligned headers.
Cause. The stylesheet is not loaded, or the wrong edition's stylesheet is.
Fix. Import it once, in your entry point or root layout: import 'super-scheduler/styles.css' for Pro, import 'super-scheduler-lite/styles.css' for Lite. The Pro stylesheet lives in @layer super-scheduler with zero-specificity selectors, so any unlayered rule of yours wins; a broad reset such as * { border: 0 } therefore removes the library's borders too (see Tailwind). unstyled turns the library's visual rules off on purpose.
The scheduler is 0 px tall or not the height you set
Symptom. Nothing is visible, or the grid is shorter or taller than expected.
Causes and fixes (Pro).
heightSpecdefaults to'Max':height(600 by default) is a ceiling, and the grid is as tall as its rows up to that value. Two rows withheight={320}render about 130 px tall. UseheightSpec="Fixed"for a constant box.height="100%"fills the component's host element, an unstyled<div>thatSuperSchedulerComponentrenders inside your wrapper. If that<div>has no height, the scheduler collapses to 0 px. Give the wrapper a definite height and the host 100%:
import { SuperSchedulerComponent } from 'super-scheduler'
import type { SuperScheduler } from 'super-scheduler'
interface Props {
readonly rooms: SuperScheduler.ResourceData[]
readonly events: SuperScheduler.EventData[]
}
// height="100%" fills the component's own host <div>: .fill sizes it (see the CSS below).
export function FillParent({ rooms, events }: Props) {
return (
<div className="fill">
<SuperSchedulerComponent
height="100%"
startDate="2026-10-01"
days={31}
scale="Day"
resources={rooms}
events={events}
/>
</div>
)
}.fill {
height: 70vh; /* or a flex item with min-height: 0 */
}
.fill > div {
height: 100%;
}heightSpec="Auto"sizes the control to its content with no vertical scrollbar, so the page scrolls instead.SchedulerPanestakes its own numericheightfor all panes together.
In Lite, height is always a fixed number of pixels (400 by default). In both editions, when the wrapper is a flex item, give it min-width: 0 in a row (or min-height: 0 in a column); otherwise the flex item's automatic minimum size can let the grid push the layout wider or taller instead of scrolling.
ref.current or control is null
Cause. The control is created in componentDidMount. During the first render, on the server and after unmount, there is no live control: ref.current is null before mount, and ref objects passed as controlRef are reset to null on unmount.
Fix. Read the control in effects and event handlers, never during render. useSchedulerControl() returns the control as state, so an effect can depend on it:
import { useEffect } from 'react'
import { SuperScheduler, SuperSchedulerComponent, useSchedulerControl } from 'super-scheduler'
const START = SuperScheduler.Date.today().addDays(-30)
export function Planning({ rooms }: { rooms: SuperScheduler.ResourceData[] }) {
// `control` is null during the first render and the live control after mount.
const { controlRef, control } = useSchedulerControl()
useEffect(() => {
if (control === null || control.disposed()) return
control.scrollTo(SuperScheduler.Date.today(), false, 'middle')
}, [control])
return (
<SuperSchedulerComponent
controlRef={controlRef}
startDate={START}
days={90}
scale="Day"
resources={rooms}
/>
)
}In Pro, a function controlRef is called with the control on mount and is not called with null on unmount. A control reference kept from before an unmount points to a disposed control: check control.disposed() in asynchronous code. With the imperative API, call init() before anything else; update() before init() throws SuperScheduler.Exception.
Nothing shows in the grid
Check these in order:
- Range.
daysdefaults to1andstartDateto today. In Pro,scalealso defaults to hourly cells ('CellDuration'with 60 minutes). SetstartDate,daysandscale="Day"to the period your data covers. Datasets fromsuper-scheduler/datasetsstart on 2026-01-01 by default. - Date strings.
'2026-10-01T10:00'throwsSchedulerDate: "2026-10-01T10:00" is not an ISO 8601 date. Use'2026-10-01'or'2026-10-01T10:00:00'. NativeDateobjects do not type-check; convert them (see civil dates). - Resource ids. An event's
resourcemust equal a resourceidexactly:1and'1'are different. Events of unknown resources are not drawn. - Trees (Pro).
childrenrender only withtreeEnabled, and a parent shows its children only when it hasexpanded: true. - Filters and flags. An active
control.events.filter()orcontrol.rows.filter(), orhidden: trueon the event, hides it. - Empty state. With no visible rows, Pro shows
emptyStateif you set it; Lite shows "No resources" by default.
Changes do not appear
- Mutated in place. The React component forwards a prop only when its identity changes. Pushing into the same
eventsorresourcesarray and re-rendering sends nothing. Pass a new array, or callcontrol.update()after an in-place edit. - The array changes by itself (Pro). The control adopts the
eventsarray you pass and splices it when events are added, removed or committed. Pass a copy (useMemo(() => events.slice(), [events])) if that array is shared state. A frozen array, as some state libraries produce in development, makescontrol.events.add,updateandremovethrowTypeError. - Updating an unknown id.
control.events.update(data)does nothing when the id is not loaded; useaddfor new events.addthrows on a duplicate id. defaultEventschanged. It is read once atinit(); later values are ignored with a development warning. Use controlledeventsfor data that changes.- Cell hooks (Pro).
onBeforeCellRenderresults are cached per cell. If a cell depends on events, setcellsAutoUpdated: trueon its resource or callcontrol.update(). - A prop removed. A prop that disappears between renders returns to its default value.
Import errors and wrong subpaths
Only these entry points exist; anything else, such as super-scheduler/dist/..., fails with a "not exported" error from your bundler or Node:
- Pro:
super-scheduler,/styles.css,/react-render,/history,/minimap,/panes,/zoom-ui,/views,/ranges,/hooks,/tailwind,/datasetsand/core. - Lite:
super-scheduler-liteandsuper-scheduler-lite/styles.cssonly. Pro modules are not part of Lite.
TypeScript resolves these through the package exports with moduleResolution set to bundler, node16 or nodenext; the older node setting works through the package's typesVersions. With noUncheckedSideEffectImports (TypeScript 5.6 and later), a stylesheet import needs a declare module '*.css' declaration, which bundler client types such as vite/client already provide. super-scheduler/tailwind is a CommonJS-style preset: load it with require('super-scheduler/tailwind'), or with a default import where your config supports CommonJS interop.
Console messages
| Message | Meaning |
|---|---|
[super-scheduler] renderEvent needs the component from "super-scheduler/react-render" | A React render prop (renderEvent, renderCell, eventHover, an onBefore*DomAdd handler...) was given to the root component. Import SuperSchedulerComponent from super-scheduler/react-render. |
super-scheduler: <feature> is not supported yet | A reserved API: typed, accepted, inert. See Reserved APIs. |
[super-scheduler] events wins over defaultEvents | Both props were given; events is used. |
[super-scheduler] defaultEvents is read only during init() | A new defaultEvents value after mount is ignored. |
SuperScheduler Lite: unsupported option "..." | Lite throws for any option it does not implement, also in production. scale must be 'Day', and resource children, frozen, split and columns require Pro. |
Pro prints these warnings only when NODE_ENV is not production, and reserved-API warnings only once per feature. Lite's errors are thrown in every build.
"Invalid hook call" or two copies of React
Symptom. "Invalid hook call" from useSchedulerControl or useScheduler, React content in render slots that cannot see your context providers, or portal errors.
Cause. The library resolves a different copy of React than your app. Both editions declare React as a peer dependency and never bundle it, so this happens when the install or a link brings a second copy: a linked or locally built package, a monorepo with several React versions, or unmet peer ranges (18.2 or later, or 19).
Fix. npm ls react react-dom must show one version. Install the Pro tarball instead of linking a local copy. In Vite, add resolve: { dedupe: ['react', 'react-dom'] }; in webpack, alias react and react-dom to your app's copies.
Content Security Policy errors
The library needs no inline scripts and no 'unsafe-inline' styles: it loads as modules and writes geometry through element.style. If the console reports violations, check three things: lazily loaded chunks must be allowed by script-src; the Pro stylesheet's small data: SVG images need img-src data:; and inline style attributes inside HTML strings you pass (html, bubbleHtml) are blocked, so use classes. Details and a sample policy are in Server rendering.
Tailwind removes borders or overrides the scheduler
Tailwind v3's preflight is unlayered and resets borders on every element, which beats the library's layered rules. Put preflight in a layer below SuperScheduler:
@layer tw-base, super-scheduler;
@import 'super-scheduler/styles.css';
@layer tw-base {
@tailwind base;
}
@tailwind components;
@tailwind utilities;With Tailwind v4, declare the layer order before the imports so the library sits between base and your utilities:
@layer theme, base, super-scheduler, components, utilities;
@import 'tailwindcss';
@import 'super-scheduler/styles.css';See Theming for the Tailwind preset and token mapping.
Other surprises
- Events snap to whole days (Pro).
useEventBoxesdefaults to'Always', which draws events over whole cells. Use'Never'to draw exact times, and addeventMoveByCellif dragging should stay anchored to cells. - A click leaves a selection behind (Pro). A click on an empty cell is a one-cell selection reported to
onTimeRangeSelectedwithorigin: 'click', and its shadow stays until the next selection or a click elsewhere. Callargs.control.clearSelection()in the handler. - Keys do nothing (Pro).
keyboardEnableddefaults tofalse.keyboardMode="Full"needs it too. With several schedulers on a page, setkeyboardTarget="component". - Dates became objects (Pro). After a drag or resize, the event's
startandendareSuperScheduler.Dateobjects.String(date)andJSON.stringifygive the civil ISO value;date.toString('d MMM', locale)formats it. - Wrong weekday.
SuperScheduler.Date#getDay()returns the day of the month. UsegetDayOfWeek()(0 is Sunday) ordayOfWeekISO()(1 is Monday).
Related guides: React integration, Controlled state, Server rendering and Virtualization and performance.