Customization

Building Blocks

The parts of the built-in sidebar are exported on their own, plus the chip that draws an event. Place them anywhere in your layout and bind them to one scheduler or several.

// The parts of the sidebar, and the chip
import { DateNavigator, SearchBox, CalendarList, DragSourceList, EventChip } from '@sv5ui/scheduler';

// The five views, for ViewDefinition.component
import { MonthView, WeekView, DayView, YearView, AgendaView } from '@sv5ui/scheduler';

DateNavigator

A month calendar with a dot under every day that has events. Picking a day calls onSelect with its start; bind the same date to a scheduler and the two stay in step. A Today link sits under the grid.

MonTueWedThuFriSatSun
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

Selected: Tuesday, October 20, 2026

October 20, 2026

Tuesday

October 20, 2026

Today GMT+7
<script lang="ts">
  import { now } from '@internationalized/date';
  let date = $state(now(zone));
</script>

<!-- Picking a day sets date; the scheduler follows because date is bound to both.
     events gives the dots: an EventInput array, series expanded for the month shown. -->
<DateNavigator {date} onSelect={(day) => (date = day)} {events} timeZone={zone} />
<Scheduler bind:events bind:date timeZone={zone} />

DateNavigator props

PropTypeDefault
dateZonedDateTimerequired
onSelect(date: ZonedDateTime) => void-
eventsEventInput<T>[][]
timeZoneTimeZoneIddevice zone
localestring'en-US'
weekStartsOnWeekDay1
labelsPick<SchedulerLabels, 'today'>English
uiPartial<Record<DateNavigatorSlots, ClassNameValue>>-
classClassNameValue-
refHTMLElement | null-

DateNavigator slots

SlotElement
rootThe section
calendarThe sv5ui Calendar
todayThe Today link under it

SearchBox and CalendarList

Two inputs to the same filters the scheduler applies. The box binds value, the list binds hiddenCalendars; pass both to the scheduler. Press / to focus the box from anywhere on this page.

/

My calendars

  • Monday, October 5

    1 event · 15m
  • Tuesday, October 6

    1 event · 15m
  • Wednesday, October 7

    1 event · 15m
  • Thursday, October 8

    1 event · 15m
  • Friday, October 9

    1 event · 15m
  • Monday, October 12

    1 event · 15m
  • Tuesday, October 13

    1 event · 15m
  • Wednesday, October 14

    1 event · 15m
  • Thursday, October 15

    1 event · 15m
  • Friday, October 16

    2 events · 1h 45m
  • Saturday, October 17

    1 event · 45m
  • Monday, October 19

    2 events · 1h 45m
  • Tuesday, October 20

    Today 3 events · 3h 15m
  • Wednesday, October 21

    4 events · 3h 15m
  • Thursday, October 22

    3 events · 1h 45m
  • Friday, October 23

    4 events · 4h 45m
  • Saturday, October 24

    1 event · 1h
  • Monday, October 26

    2 events · 1h 15m
  • Tuesday, October 27

    1 event · 15m
  • Wednesday, October 28

    2 events · 15m
  • Thursday, October 29

    1 event · 15m
  • Friday, October 30

    1 event · 15m
<script lang="ts">
  let search = $state('');
</script>

<!-- bind:value here, search there. The / key focuses the box from anywhere on the
     page unless another input has focus; shortcut={null} turns that off. Other input
     attributes (name, autocomplete, data-*) pass through to the input. -->
<SearchBox bind:value={search} shortcut="/" name="q" autocomplete="off" />
<Scheduler bind:events {search} />

SearchBox props

Other input attributes (name, autocomplete, data-*) pass through to the input.

PropTypeDefault
valuestring''
shortcutstring | null'/'
labelsPick<SchedulerLabels, 'searchEvents'>English
uiPartial<Record<SearchBoxSlots, ClassNameValue>>-
classClassNameValue-
refHTMLInputElement | null-

SearchBox slots

SlotElement
rootThe sv5ui Input
shortcutThe Kbd hint at the end
<script lang="ts">
  let hiddenCalendars = $state<string[]>([]);
</script>

<!-- The list toggles ids in hiddenCalendars; the scheduler hides those calendars.
     title={null} drops the heading when the list sits under one of your own. -->
<CalendarList {calendars} bind:hiddenCalendars title={null} />
<Scheduler bind:events {calendars} {hiddenCalendars} />

CalendarList props

PropTypeDefault
calendarsSchedulerCalendar[]required
hiddenCalendarsstring[][]
titlestring | nulllabels.calendars
labelsPick<SchedulerLabels, 'calendars'>English
itemSnippet<[CalendarListItemProps]>a Checkbox row
uiPartial<Record<CalendarListSlots, ClassNameValue>>-
classClassNameValue-
refHTMLElement | null-

CalendarList slots

SlotElement
rootThe section
titleThe heading
listThe ul
itemEach li
labelThe label around checkbox and text
swatchThe colour dot
nameThe calendar title

Your Own Rows

The item snippet replaces each row of the list. The list still owns the state and hands you toggle.

My calendars

  • Work
  • Team
  • Personal
  • Travel
  • Learning
  • Birthdays

Hidden: personal

<script lang="ts">
  import { Switch } from 'sv5ui';
  import { CalendarList, type CalendarListItemProps } from '@sv5ui/scheduler';

  // Tailwind ships only the classes it can see, so map colours to classes
  const swatch: Record<string, string> = { primary: 'bg-primary', info: 'bg-info', success: 'bg-success' /* ... */ };
</script>

<!-- item replaces each row; the list keeps the state and gives you toggle -->
<CalendarList {calendars} bind:hiddenCalendars item={row} />

{#snippet row({ calendar, visible, toggle }: CalendarListItemProps)}
  <div class="flex items-center justify-between py-1">
    <span class="flex items-center gap-2 text-sm">
      <span class={['size-2.5 rounded-full', swatch[calendar.color ?? 'primary']]}></span>
      {calendar.title}
    </span>
    <Switch checked={visible} onCheckedChange={toggle} size="sm" />
  </div>
{/snippet}

CalendarListItemProps

PropTypeDefault
calendarSchedulerCalendar-
visibleboolean-
toggle() => void-

DragSourceList

The backlog: a list of things not yet on the calendar, each row a dragSource. Drop one on any scheduler on the page and it becomes an event through onMutate; see Drag & Drop. The rows below use the item snippet.

Unscheduled

October 20, 2026

Tuesday

October 20, 2026

Today GMT+7
No events
<!-- The backlog. Each row is a dragSource; drop it on any scheduler on the page.
     calendars colours the swatch from item.calendarId. -->
<DragSourceList items={backlog} {calendars} />
<Scheduler bind:events view="day" />

<!-- item replaces the content of each draggable row -->
{#snippet row({ item, color }: DragSourceListItemProps)}
  <Badge label={item.title} {color} variant="soft" />
  <span class="text-xs">{item.durationMinutes ?? 60} min</span>
{/snippet}

DragSourceList props

PropTypeDefault
itemsDragSourceData<T>[]required
calendarsSchedulerCalendar[][]
titlestring | nulllabels.unscheduled
labelsPick<SchedulerLabels, 'unscheduled' | 'duration'>English
itemSnippet<[DragSourceListItemProps<T>]>title and duration
uiPartial<Record<DragSourceListSlots, ClassNameValue>>-
classClassNameValue-
refHTMLElement | null-

DragSourceListItemProps

PropTypeDefault
itemDragSourceData<T>-
colorEventColor-

DragSourceList slots

SlotElement
rootThe section
titleThe heading
listThe ul
itemEach draggable row
swatchThe colour dot
nameThe title
durationThe duration text

EventChip

The chip the grid draws, on its own. Eight colours, soft or solid, two sizes. Its shape follows position: a time position is a block with the time under the title, a span position or none is a pill. It does not look up calendars, so pass color yourself.

<!-- The chip on its own: eight colours, soft or solid, sm or md. position decides the
     shape: a time position draws a block with the time, a span position a pill.
     Without position it is a pill. It does not read calendars: pass color. -->
<EventChip {event} color="success" variant="solid" />
<EventChip {event} color="warning" size="sm" showTime={false} />

<!-- children replace the title and time -->
<EventChip {event} color="info">
  <Icon name="lucide:video" size="12" /> {event.title}
</EventChip>

EventChip props

Other button attributes pass through to the button.

PropTypeDefault
eventSchedulerEvent<T>required
positionPositionedEvent<T>-
colorEventColorevent.color, else primary
variant'soft' | 'solid''soft'
size'sm' | 'md''md'
showTimebooleantrue
selectedbooleanfalse
draggingbooleanfalse
localestring'en-US'
hour12booleanlocale default
childrenSnippet-
uiPartial<Record<EventChipSlots, ClassNameValue>>-
classClassNameValue-
refHTMLElement | null-

EventChip slots

SlotElement
rootThe button, with data-sch-event-id
swatchThe colour dot of a pill
titleThe title
timeThe time line

A Layout of Your Own

One set of state, three parts, two calendars. The navigator, the search box and the list read and write the same variables as both grids, so a pick or a query changes everything at once. The code shows a two-column page layout; here the parts sit above the grids because the docs column is narrow.

MonTueWedThuFriSatSun
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

My calendars

GMT+7
Mon 19
Tue 20
Wed 21
Thu 22
Fri 23
Sat 24
Sun 25
All day
Lunch break
  • Monday, October 5

    1 event · 15m
  • Tuesday, October 6

    1 event · 15m
  • Wednesday, October 7

    1 event · 15m
  • Thursday, October 8

    1 event · 15m
  • Friday, October 9

    1 event · 15m
  • Monday, October 12

    1 event · 15m
  • Tuesday, October 13

    1 event · 15m
  • Wednesday, October 14

    1 event · 15m
  • Thursday, October 15

    1 event · 15m
  • Friday, October 16

    2 events · 1h 45m
  • Saturday, October 17

    1 event · 45m
  • Monday, October 19

    2 events · 1h 45m
  • Tuesday, October 20

    Today 3 events · 3h 15m
  • Wednesday, October 21

    4 events · 3h 15m
  • Thursday, October 22

    3 events · 1h 45m
  • Friday, October 23

    4 events · 4h 45m
  • Saturday, October 24

    1 event · 1h
  • Monday, October 26

    2 events · 1h 15m
  • Tuesday, October 27

    1 event · 15m
  • Wednesday, October 28

    2 events · 15m
  • Thursday, October 29

    1 event · 15m
  • Friday, October 30

    1 event · 15m
<script lang="ts">
  import { now } from '@internationalized/date';
  import { CalendarList, DateNavigator, Scheduler, SearchBox } from '@sv5ui/scheduler';

  let events = $state<EventInput[]>([/* ... */]);
  let date = $state(now(zone));
  let search = $state('');
  let hiddenCalendars = $state<string[]>([]);
</script>

<!-- One set of state, three parts, two calendars. Every part reads and writes the
     same variables, so the navigator, the search box and the list drive both grids. -->
<div class="grid gap-4 lg:grid-cols-[16rem_1fr]">
  <aside class="space-y-4">
    <DateNavigator {date} onSelect={(day) => (date = day)} {events} timeZone={zone} />
    <SearchBox bind:value={search} />
    <CalendarList {calendars} bind:hiddenCalendars />
  </aside>
  <div class="space-y-4">
    <Scheduler bind:events bind:date {calendars} {hiddenCalendars} {search} timeZone={zone} toolbar={false} height={480} />
    <Scheduler bind:events bind:date {calendars} {hiddenCalendars} {search} timeZone={zone} view="agenda" toolbar={false} height={320} />
  </div>
</div>

The Five Views

MonthView, WeekView, DayView, YearView and AgendaView are exported so a view definition of yours can reuse them with a different range. They expect the full set of props the scheduler passes, so they are not for rendering on their own. See Custom Views & Layouts.

// The five view components are exported for one purpose: ViewDefinition.component.
// They need the full ViewProps the scheduler passes, so they are not rendered directly.
import { WeekView, type ViewDefinition } from '@sv5ui/scheduler';

const workWeek: ViewDefinition = {
  name: 'workweek',
  label: 'Work week',
  layout: 'time-grid',
  range: (anchor) => ({ start: monday(anchor), end: monday(anchor).add({ days: 5 }) }),
  step: (anchor, direction) => anchor.add({ weeks: direction }),
  component: WeekView
};

Props

The Scheduler props the parts on this page bind to.

PropTypeDefault
dateZonedDateTimetoday
searchstring''
hiddenCalendarsstring[][]
calendarsSchedulerCalendar[][]
eventsEventInput<T>[]-
viewsViewDefinition<T>[][]

Snippets

The item snippets of the two lists.

TypeFields
CalendarListItemPropscalendar, visible, toggle
DragSourceListItemPropsitem, color