Customization

Toolbar

The bar above the grid: Today, the arrows, the title and the view switcher. Add to it, hide it, or replace it with your own and keep every function it had.

The Built-in Toolbar

Parts

PartWhat it does
MenuShows when there is a sidebar or an onMenu handler. Toggles the sidebar, then calls onMenu
TodayBack to now, keeping the view. Label from labels.today
Previous, NextOne period of the current view. Named labels.previous and labels.next
TitleThe view's title(), or shortTitle() when compact. A polite live region, so a screen reader hears the period change
View switchersv5ui Tabs, a Select when compact. A group named labels.views. Lists built-in views and registered ones, in order
ActionsYour toolbarActions snippet, in a group named labels.actions

Extra Controls

toolbarActions renders at the end of the built-in toolbar. It takes no arguments: it is your markup, bound to your state. The demo filters by calendar with a toggle group and adds a Create button.

Oct 19 – 25, 2026

GMT+7
Mon 19
Tue 20
Wed 21
Thu 22
Fri 23
Sat 24
Sun 25
All day
Lunch break
<script lang="ts">
  import { Button, ToggleGroup } from 'sv5ui';
  let scope = $state('all');
</script>

<!-- toolbarActions renders at the end of the built-in toolbar, in a group named
     labels.actions. It takes no arguments: it is your markup, bound to your state. -->
<Scheduler
  bind:events
  toolbarActions={actions}
  filter={(event) => scope === 'all' || event.calendarId === scope}
>
  {#snippet actions()}
    <ToggleGroup
      items={[{ value: 'all', label: 'All' }, { value: 'work', label: 'Work' }, { value: 'personal', label: 'Personal' }]}
      bind:value={scope}
      size="sm"
    />
    <Button label="Create event" icon="lucide:plus" size="sm" onclick={createNow} />
  {/snippet}
</Scheduler>

Hiding It

toolbar={false} removes the bar. The grid still reacts to view and date, so bind them and navigate from outside, as the demo two sections down does.

<!-- No toolbar at all. Navigate with bind:view and bind:date from outside. -->
<Scheduler bind:events bind:view bind:date toolbar={false} />

Your Own Toolbar

Pass a snippet as toolbar and it renders where the built-in one was, inside the scheduler. Everything the built-in bar knows arrives as arguments. The demo leaves the Year view out of the switcher and hides the switcher when compact.

Oct 19 – 25, 2026

GMT+7
Mon 19
Tue 20
Wed 21
Thu 22
Fri 23
Sat 24
Sun 25
All day
Lunch break
<script lang="ts">
  import { Button, ToggleGroup } from 'sv5ui';
  import { Scheduler, type ToolbarSnippetProps } from '@sv5ui/scheduler';
</script>

<!-- Your toolbar, rendered where the built-in one was. Everything you need arrives
     as arguments; the Year view is left out of the switcher here. -->
<Scheduler bind:events toolbar={header} />

{#snippet header({ title, step, today, view, views, setView, compact }: ToolbarSnippetProps)}
  <div class="flex flex-wrap items-center gap-3 border-b border-outline-variant/60 px-4 py-3">
    <Button icon="lucide:chevron-left" aria-label="Previous" variant="ghost" color="surface" size="sm" onclick={() => step(-1)} />
    <Button label="Today" variant="outline" color="surface" size="sm" onclick={today} />
    <Button icon="lucide:chevron-right" aria-label="Next" variant="ghost" color="surface" size="sm" onclick={() => step(1)} />
    <h2 class="text-lg font-semibold">{title}</h2>
    {#if !compact}
      <ToggleGroup
        class="ms-auto"
        items={views.filter(({ name }) => name !== 'year').map(({ name, label }) => ({ value: name, label }))}
        value={view}
        onValueChange={(value: string | undefined) => value && setView(value)}
        size="sm"
      />
    {/if}
  </div>
{/snippet}
interface ToolbarSnippetProps {
  title: string;                         // 'Sep 14 – 20, 2026', from the view's title()
  date: ZonedDateTime;                   // the anchor
  range: DateRange;                      // what is on screen, after hiddenDays
  view: string;                          // the current view name
  views: { name: string; label: string }[];  // built-in and registered, in switcher order
  scheduler: SchedulerContext;           // timeZone, locale, labels, compact, now...
  step: (direction: 1 | -1) => void;     // one period back or forward
  today: () => void;
  navigate: (date: ZonedDateTime, view?: string) => void;
  setView: (name: string) => void;
  toggleSidebar: () => void;
  sidebarOpen: boolean;
  compact: boolean;                      // below compactBreakpoint
}

Titles

title comes from the view definition's title(), or shortTitle() when compact, and describes the days actually on screen: with hiddenDays hiding the weekend, a week reads "Sep 14 – 18". Use compact to change the layout of your own bar on a phone: drop the switcher into a menu, shorten the buttons.

Controls Outside the Scheduler

The snippet is for a bar inside the calendar. For controls in your page header, beside a search box or a user menu, skip the snippet: hide the toolbar, bind view and date, and change them from anywhere.

GMT+7
Mon 19
Tue 20
Wed 21
Thu 22
Fri 23
Sat 24
Sun 25
All day
Lunch break
<script lang="ts">
  import { now } from '@internationalized/date';
  let view = $state('week');
  let date = $state(now(zone));
</script>

<!-- The toolbar snippet renders inside the scheduler. For controls in your own page
     header, above or beside the calendar, skip the snippet: bind view and date and
     drive them from wherever you like. -->
<header class="flex items-center gap-2">
  <Button label="Today" onclick={() => (date = now(zone))} />
  <Button icon="lucide:chevron-left" onclick={() => (date = date.subtract({ weeks: 1 }))} />
  <Button icon="lucide:chevron-right" onclick={() => (date = date.add({ weeks: 1 }))} />
  <Select items={viewItems} bind:value={view} />
</header>

<Scheduler bind:events bind:view bind:date toolbar={false} />

The Menu Button

With a sidebar, the menu button toggles it and then calls onMenu. Without a sidebar the button appears only when onMenu is given, so an app can open its own navigation drawer from the calendar's bar. The demo counts the presses: 0.

Oct 19 – 25, 2026

GMT+7
Mon 19
Tue 20
Wed 21
Thu 22
Fri 23
Sat 24
Sun 25
All day
Lunch break
<!-- With a sidebar, the menu button toggles it and onMenu is called after the
     toggle. Without a sidebar the button shows only when onMenu is given, so an
     app can open its own navigation drawer from the calendar's toolbar. -->
<Scheduler bind:events onMenu={() => (drawerOpen = true)} />
<Drawer bind:open={drawerOpen}>...</Drawer>

<!-- In a custom toolbar, toggleSidebar and sidebarOpen do the same job -->
{#snippet header({ toggleSidebar, sidebarOpen }: ToolbarSnippetProps)}
  <Button icon="lucide:menu" aria-label="Menu" aria-expanded={sidebarOpen} onclick={toggleSidebar} />
{/snippet}

Props

The toolbar props, and the two bindings that replace it from outside.

PropTypeDefault
toolbarboolean | Snippet<[ToolbarSnippetProps]>true
toolbarActionsSnippet-
onMenu() => void-
viewstring'week'
dateZonedDateTimetoday
filter(event: SchedulerEvent<T>) => boolean-

Snippets

What the toolbar snippet receives.

TypeFields
ToolbarSnippetPropstitle, date, range, view, views, scheduler, step, today, navigate, setView, toggleSidebar, sidebarOpen, compact