# Fehlerbehebung

> Typische Integrationsprobleme lösen: fehlende Styles, Host ohne Höhe, null-Refs, leere Ansichten, falsche Imports, doppeltes React, CSP-Fehler und Tailwind.

Source: https://superscheduler.org/de/docs/troubleshooting/
Reviewed: 2026-10-07

Die meisten Probleme haben wenige Ursachen: Das Stylesheet ist nicht importiert, der Host hat keine Höhe für `height="100%"`, das Control wird vor dem Mount gelesen, oder die Ansicht zeigt einen Tag ab heute, während die Daten woanders liegen (`days` ist standardmäßig 1 und `startDate` heute). Prüfen Sie außerdem, dass Datums-Strings Sekunden enthalten, dass die `resource`-Werte der Ereignisse genau den Ressourcen-IDs entsprechen und dass nur eine Kopie von React installiert ist. Jeder Abschnitt unten nennt Symptom, Ursache und Lösung.

Suchen Sie das Symptom, prüfen Sie die Ursache, wenden Sie die Lösung an. Die Abschnitte gelten für Pro und Lite, sofern sie keine Edition nennen. Steht Ihr Problem nicht hier, führt die [API-Referenz](https://superscheduler.org/de/docs/api-reference/) jede implementierte Option mit ihrem Standardwert auf, und ihr Abschnitt zu [reservierten APIs](https://superscheduler.org/de/docs/api-reference/#reserved) nennt, was typisiert, aber nicht implementiert ist.

## Der Planer erscheint ohne Styles
**Symptom.** Zeilen und Ereignisse erscheinen, aber ohne Rasterlinien, Farben oder ausgerichtete Köpfe.

**Ursache.** Das Stylesheet ist nicht geladen, oder es ist das Stylesheet der falschen Edition geladen.

**Lösung.** Importieren Sie es einmal, in Ihrem Einstiegspunkt oder Root-Layout: `import 'super-scheduler/styles.css'` für Pro, `import 'super-scheduler-lite/styles.css'` für Lite. Das Pro-Stylesheet liegt in `@layer super-scheduler` mit Selektoren ohne Spezifität, sodass jede Ihrer Regeln außerhalb eines Layers gewinnt; ein breiter Reset wie `* { border: 0 }` entfernt daher auch die Rahmen der Bibliothek (siehe [Tailwind](#tailwind)). `unstyled` schaltet die visuellen Regeln der Bibliothek absichtlich ab.

## Der Planer ist 0 px hoch oder nicht so hoch wie eingestellt
**Symptom.** Nichts ist zu sehen, oder das Raster ist niedriger oder höher als erwartet.

**Ursachen und Lösungen (Pro).**

- `heightSpec` steht standardmäßig auf `'Max'`: `height` (standardmäßig 600) ist eine Obergrenze, und das Raster ist so hoch wie seine Zeilen, bis zu diesem Wert. Zwei Zeilen mit `height={320}` werden etwa 130 px hoch gerendert. Verwenden Sie `heightSpec="Fixed"` für einen Kasten mit konstanter Höhe.
- `height="100%"` füllt das Host-Element der Komponente, ein ungestyltes `<div>`, das `SuperSchedulerComponent` innerhalb Ihres Wrappers rendert. Hat dieses `<div>` keine Höhe, fällt der Planer auf 0 px zusammen. Geben Sie dem Wrapper eine feste Höhe und dem Host 100 %:

```tsx
// src/FillParent.tsx
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>
  )
}
```
```css
.fill {
  height: 70vh; /* or a flex item with min-height: 0 */
}
.fill > div {
  height: 100%;
}
```

- `heightSpec="Auto"` bemisst das Control nach seinem Inhalt, ohne vertikale Bildlaufleiste; stattdessen scrollt die Seite.
- `SchedulerPanes` nimmt eine eigene numerische `height` für alle Bereiche zusammen.

In Lite ist `height` immer eine feste Pixelzahl (standardmäßig 400). Wenn der Wrapper ein Flex-Element ist, geben Sie ihm in beiden Editionen `min-width: 0` in einer Zeile (oder `min-height: 0` in einer Spalte); sonst kann die automatische Mindestgröße des Flex-Elements dazu führen, dass das Raster das Layout verbreitert oder verlängert, statt zu scrollen.

## `ref.current` oder `control` ist null
**Ursache.** Das Control wird in `componentDidMount` erzeugt. Beim ersten Rendern, auf dem Server und nach dem Unmount gibt es kein aktives Control: `ref.current` ist vor dem Mount `null`, und Ref-Objekte, die als `controlRef` übergeben werden, werden beim Unmount auf `null` zurückgesetzt.

**Lösung.** Lesen Sie das Control in Effects und Event-Handlern, nie während des Renderns. `useSchedulerControl()` gibt das Control als State zurück, sodass ein Effect davon abhängen kann:

```tsx
// src/Planning.tsx
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 wird eine Funktion als `controlRef` beim Mount mit dem Control aufgerufen und beim Unmount nicht mit `null`. Eine Referenz auf das Control, die aus der Zeit vor einem Unmount stammt, zeigt auf ein freigegebenes Control: Prüfen Sie in asynchronem Code `control.disposed()`. Mit der imperativen API rufen Sie vor allem anderen `init()` auf; `update()` vor `init()` wirft `SuperScheduler.Exception`.

## Im Raster ist nichts zu sehen
Prüfen Sie diese Punkte der Reihe nach:

1. **Zeitraum.** `days` ist standardmäßig `1` und `startDate` heute. In Pro steht `scale` außerdem standardmäßig auf Stundenzellen (`'CellDuration'` mit 60 Minuten). Setzen Sie `startDate`, `days` und `scale="Day"` auf den Zeitraum, den Ihre Daten abdecken. Datensätze aus `super-scheduler/datasets` beginnen standardmäßig am 2026-01-01.
2. **Datums-Strings.** `'2026-10-01T10:00'` wirft `SchedulerDate: "2026-10-01T10:00" is not an ISO 8601 date`. Verwenden Sie `'2026-10-01'` oder `'2026-10-01T10:00:00'`. Native `Date`-Objekte bestehen die Typprüfung nicht; wandeln Sie sie um (siehe [bürgerliche Zeit](https://superscheduler.org/de/docs/locales-dates-timezones/#civil-dates)).
3. **Ressourcen-IDs.** Die `resource` eines Ereignisses muss genau einer Ressourcen-`id` entsprechen: `1` und `'1'` sind verschieden. Ereignisse unbekannter Ressourcen werden nicht gezeichnet.
4. **Bäume (Pro).** `children` werden nur mit `treeEnabled` gerendert, und ein Elternelement zeigt seine Kinder nur, wenn es `expanded: true` hat.
5. **Filter und Flags.** Ein aktives `control.events.filter()` oder `control.rows.filter()` oder `hidden: true` am Ereignis blendet es aus.
6. **Leerzustand.** Ohne sichtbare Zeilen zeigt Pro `emptyState`, falls Sie es gesetzt haben; Lite zeigt standardmäßig „No resources“.

## Änderungen erscheinen nicht
- **Direkt verändert.** Die React-Komponente leitet eine Prop nur weiter, wenn sich ihre Identität ändert. Etwas in dasselbe `events`- oder `resources`-Array zu pushen und neu zu rendern, sendet nichts. Übergeben Sie ein neues Array oder rufen Sie nach einer direkten Änderung `control.update()` auf.
- **Das Array ändert sich von selbst (Pro).** Das Control übernimmt das `events`-Array, das Sie übergeben, und verändert es per splice, wenn Ereignisse hinzugefügt, entfernt oder übernommen werden. Übergeben Sie eine Kopie (`useMemo(() => events.slice(), [events])`), wenn dieses Array geteilter State ist. Ein eingefrorenes Array, wie es manche State-Bibliotheken in der Entwicklung erzeugen, lässt `control.events.add`, `update` und `remove` einen `TypeError` werfen.
- **Eine unbekannte ID aktualisieren.** `control.events.update(data)` bewirkt nichts, wenn die ID nicht geladen ist; verwenden Sie `add` für neue Ereignisse. `add` wirft bei einer doppelten ID.
- **`defaultEvents` geändert.** Es wird einmal bei `init()` gelesen; spätere Werte werden mit einer Warnung in der Entwicklung ignoriert. Verwenden Sie kontrollierte `events` für Daten, die sich ändern.
- **Zell-Hooks (Pro).** Ergebnisse von `onBeforeCellRender` werden pro Zelle zwischengespeichert. Hängt eine Zelle von Ereignissen ab, setzen Sie `cellsAutoUpdated: true` an ihrer Ressource oder rufen Sie `control.update()` auf.
- **Eine Prop entfernt.** Eine Prop, die zwischen zwei Renderings verschwindet, kehrt zu ihrem Standardwert zurück.

## Importfehler und falsche Subpaths
Nur diese Einstiegspunkte existieren; alles andere, etwa `super-scheduler/dist/...`, scheitert mit einem „not exported“-Fehler Ihres Bundlers oder von Node:

- Pro: `super-scheduler`, `/styles.css`, `/react-render`, `/history`, `/minimap`, `/panes`, `/zoom-ui`, `/views`, `/ranges`, `/hooks`, `/tailwind`, `/datasets` und `/core`.
- Lite: nur `super-scheduler-lite` und `super-scheduler-lite/styles.css`. Pro-Module sind nicht Teil von Lite.

TypeScript löst diese über die `exports` des Pakets auf, wenn `moduleResolution` auf `bundler`, `node16` oder `nodenext` steht; die ältere Einstellung `node` funktioniert über die `typesVersions` des Pakets. Mit `noUncheckedSideEffectImports` (TypeScript 5.6 und neuer) braucht ein Stylesheet-Import eine Deklaration `declare module '*.css'`, die Client-Typen von Bundlern wie `vite/client` bereits mitbringen. `super-scheduler/tailwind` ist ein Preset im CommonJS-Stil: Laden Sie es mit `require('super-scheduler/tailwind')` oder mit einem Default-Import, wo Ihre Konfiguration CommonJS-Interop unterstützt.

## Konsolenmeldungen
| Meldung | Bedeutung |
|---|---|
| `[super-scheduler] renderEvent needs the component from "super-scheduler/react-render"` | Eine React-Render-Prop (`renderEvent`, `renderCell`, `eventHover`, ein `onBefore*DomAdd`-Handler...) wurde an die Wurzelkomponente übergeben. Importieren Sie `SuperSchedulerComponent` aus `super-scheduler/react-render`. |
| `super-scheduler: <feature> is not supported yet` | Eine reservierte API: typisiert, akzeptiert, wirkungslos. Siehe [Reservierte APIs](https://superscheduler.org/de/docs/api-reference/#reserved). |
| `[super-scheduler] events wins over defaultEvents` | Beide Props wurden übergeben; `events` wird verwendet. |
| `[super-scheduler] defaultEvents is read only during init()` | Ein neuer Wert für `defaultEvents` nach dem Mount wird ignoriert. |
| `SuperScheduler Lite: unsupported option "..."` | Lite wirft bei jeder Option, die es nicht implementiert, auch in Produktion. `scale` muss `'Day'` sein, und `children`, `frozen`, `split` und `columns` von Ressourcen erfordern Pro. |

Pro gibt diese Warnungen nur aus, wenn `NODE_ENV` nicht `production` ist, und Warnungen zu reservierten APIs nur einmal pro Funktion. Die Fehler von Lite werden in jedem Build geworfen.

## „Invalid hook call“ oder zwei Kopien von React
**Symptom.** „Invalid hook call“ aus `useSchedulerControl` oder `useScheduler`, React-Inhalte in Render-Slots, die Ihre Context-Provider nicht sehen, oder Portal-Fehler.

**Ursache.** Die Bibliothek löst eine andere Kopie von React auf als Ihre App. Beide Editionen deklarieren React als Peer-Abhängigkeit und bündeln es nie; das passiert also, wenn die Installation oder ein Link eine zweite Kopie mitbringt: ein verlinktes oder lokal gebautes Paket, ein Monorepo mit mehreren React-Versionen oder nicht erfüllte Peer-Bereiche (18.2 oder neuer, oder 19).

**Lösung.** `npm ls react react-dom` muss eine einzige Version zeigen. Installieren Sie den Pro-Tarball, statt eine lokale Kopie zu verlinken. In Vite ergänzen Sie `resolve: { dedupe: ['react', 'react-dom'] }`; in webpack setzen Sie Aliase von `react` und `react-dom` auf die Kopien Ihrer App.

## Fehler durch die Content Security Policy
Die Bibliothek braucht keine Inline-Skripte und keine `'unsafe-inline'`-Styles: Sie lädt als Module und schreibt Geometrie über `element.style`. Meldet die Konsole Verstöße, prüfen Sie drei Dinge: Nachgeladene Chunks müssen von `script-src` erlaubt sein; die kleinen `data:`-SVG-Bilder des Pro-Stylesheets brauchen `img-src data:`; und Inline-`style`-Attribute in HTML-Strings, die Sie übergeben (`html`, `bubbleHtml`), werden blockiert, verwenden Sie also Klassen. Details und eine Beispiel-Policy finden Sie unter [Server-Rendering](https://superscheduler.org/de/docs/ssr-prerender/#csp).

## Tailwind entfernt Rahmen oder überschreibt den Planer
Das Preflight von Tailwind v3 liegt in keinem Layer und setzt die Rahmen jedes Elements zurück, was die Regeln der Bibliothek in ihrem Layer schlägt. Legen Sie das Preflight in einen Layer unterhalb von SuperScheduler:

```css
@layer tw-base, super-scheduler;

@import 'super-scheduler/styles.css';

@layer tw-base {
  @tailwind base;
}
@tailwind components;
@tailwind utilities;
```

Mit Tailwind v4 deklarieren Sie die Reihenfolge der Layer vor den Imports, damit die Bibliothek zwischen `base` und Ihren Utilities liegt:

```css
@layer theme, base, super-scheduler, components, utilities;

@import 'tailwindcss';
@import 'super-scheduler/styles.css';
```

Zum Tailwind-Preset und zur Zuordnung der Tokens siehe [Theming](https://superscheduler.org/de/docs/theming/).

## Weitere Überraschungen
- **Ereignisse rasten auf ganze Tage ein (Pro).** `useEventBoxes` steht standardmäßig auf `'Always'` und zeichnet Ereignisse über ganze Zellen. Verwenden Sie `'Never'`, um exakte Zeiten zu zeichnen, und ergänzen Sie `eventMoveByCell`, wenn das Ziehen an Zellen verankert bleiben soll.
- **Ein Klick hinterlässt eine Auswahl (Pro).** Ein Klick auf eine leere Zelle ist eine Auswahl von einer Zelle, die an `onTimeRangeSelected` mit `origin: 'click'` gemeldet wird, und ihr Schatten bleibt bis zur nächsten Auswahl oder einem Klick an anderer Stelle stehen. Rufen Sie im Handler `args.control.clearSelection()` auf.
- **Tasten bewirken nichts (Pro).** `keyboardEnabled` steht standardmäßig auf `false`. Auch `keyboardMode="Full"` braucht es. Bei mehreren Planern auf einer Seite setzen Sie `keyboardTarget="component"`.
- **Datumswerte wurden zu Objekten (Pro).** Nach einem Verschieben oder einer Dauer-Änderung sind `start` und `end` des Ereignisses `SuperScheduler.Date`-Objekte. `String(date)` und `JSON.stringify` liefern den ISO-Wert in bürgerlicher Zeit; `date.toString('d MMM', locale)` formatiert ihn.
- **Falscher Wochentag.** `SuperScheduler.Date#getDay()` gibt den Tag des Monats zurück. Verwenden Sie `getDayOfWeek()` (0 ist Sonntag) oder `dayOfWeekISO()` (1 ist Montag).

Verwandte Anleitungen: [React-Integration](https://superscheduler.org/de/docs/react-integration/), [Kontrollierter Zustand](https://superscheduler.org/de/docs/controlled-state/), [Server-Rendering](https://superscheduler.org/de/docs/ssr-prerender/) und [Virtualisierung und Performance](https://superscheduler.org/de/docs/performance-virtualization/).
