Calendar

SvCalendar: a themeable month/year/decade calendar with single / range / week / multi selection, min-max, restricted + important dates and week numbers. The same component SvGrid mounts to edit a date cell - and usable standalone in any SvGrid app.

A live, editable Svelte 5 component example from the SvGrid gallery (Date & Time). Read the SvGrid UI component docs for the full API.

About this example

SvCalendar, the themeable Svelte 5 calendar and date picker that is also the SvGrid date cell editor. It offers month, year and decade views, single, range, week and multi selection, min and max bounds, restricted and important dates, week numbers, a footer, animated navigation, mouse-wheel month scrolling and date tooltips, all styled by the --sg-* theme tokens and usable standalone in any app that has @svgrid/grid installed.

SvCalendar - a standalone, themeable calendar that is ALSO the SvGrid date cell editor. Everything here is the same component the grid mounts when you edit a date column; drop it into any app that already uses SvGrid.

Imports, features and API used

Imports: @svgrid/grid

Frequently asked questions

How do I switch between single, range and multi selection?

Set selectionMode to one, zeroOrOne, range, week, many or one of the other modes such as oneExtended for Shift-extend. onChange always delivers an array of dates, one entry for single selection and two for a range.

How do I block certain dates?

Pass restrictedDates as an array of dates or a predicate such as one that rejects weekends; those days render disabled and cannot be picked. importantDates marks days with a dot instead.

Does the calendar follow the grid theme?

Yes. It reads the same --sg-* tokens as the grid, so a theme preset or dark mode applied to the page styles the calendar identically whether it is standalone or inside a cell editor.

Related documentation

Related articles

Source code (250-calendar.svelte)

<script lang="ts">
  /**
   * SvCalendar - a standalone, themeable calendar that is ALSO the SvGrid date
   * cell editor. Everything here is the same component the grid mounts when you
   * edit a date column; drop it into any app that already uses SvGrid.
   */
  import { SvCalendar } from '@svgrid/grid'
  import type { SelectionMode, CalendarAnimation } from '@svgrid/grid'

  let mode = $state<SelectionMode>('one')
  let weekNumbers = $state(false)
  let footer = $state(true)
  let firstDayOfWeek = $state(0)
  let months = $state(1)
  let noWeekends = $state(false)
  let animate = $state<CalendarAnimation>('slide')
  let wheelNavigation = $state(true)
  let value = $state<Date[]>([new Date()])

  const fmt = new Intl.DateTimeFormat(undefined, { dateStyle: 'medium' })
  const y = new Date().getFullYear()
  const m = new Date().getMonth()
  const importantDates = [new Date(y, m, 14), new Date(y, m, 25)]
  // A tooltip for the important days (Smart-style importantDatesTemplate).
  const labels: Record<number, string> = { 14: 'Team offsite', 25: 'Release day' }
  const dateTooltip = (d: Date) =>
    d.getMonth() === m ? labels[d.getDate()] : undefined
  const restrictWeekends = (d: Date) => noWeekends && (d.getDay() === 0 || d.getDay() === 6)

  const modes: SelectionMode[] = ['one', 'zeroOrOne', 'many', 'range', 'week', 'oneExtended']
  const anims: CalendarAnimation[] = ['slide', 'fade', 'none']
</script>

<div class="wrap">
  <header>
    <h2>SvCalendar</h2>
    <p>
      A framework-free date engine behind a themed Svelte 5 view. It works standalone (below) and is
      the <strong>date cell editor inside SvGrid</strong> - the exact same component. Every
      <code>--sg-*</code> theme applies automatically.
    </p>
  </header>

  <div class="controls">
    <label>Selection
      <select bind:value={mode}>
        {#each modes as m (m)}<option value={m}>{m}</option>{/each}
      </select>
    </label>
    <label>First day
      <select bind:value={firstDayOfWeek}>
        <option value={0}>Sunday</option>
        <option value={1}>Monday</option>
      </select>
    </label>
    <label>Panels
      <select bind:value={months}>
        <option value={1}>1</option><option value={2}>2</option><option value={3}>3</option>
      </select>
    </label>
    <label>Animation
      <select bind:value={animate}>
        {#each anims as a (a)}<option value={a}>{a}</option>{/each}
      </select>
    </label>
    <label class="chk"><input type="checkbox" bind:checked={wheelNavigation} /> Mouse-wheel nav</label>
    <label class="chk"><input type="checkbox" bind:checked={weekNumbers} /> Week numbers</label>
    <label class="chk"><input type="checkbox" bind:checked={footer} /> Footer</label>
    <label class="chk"><input type="checkbox" bind:checked={noWeekends} /> Block weekends</label>
  </div>

  <div class="stage">
    <SvCalendar
      value={value}
      selectionMode={mode}
      {weekNumbers}
      {footer}
      {firstDayOfWeek}
      {months}
      {animate}
      {wheelNavigation}
      {dateTooltip}
      importantDates={importantDates}
      restrictedDates={restrictWeekends}
      onChange={(dates) => (value = dates)}
    />

    <aside class="readout">
      <h3>Selected value</h3>
      {#if value.length === 0}
        <p class="empty">Nothing selected</p>
      {:else if value.length <= 3}
        <ul>{#each value as d (d.getTime())}<li>{fmt.format(d)}</li>{/each}</ul>
      {:else}
        <p><strong>{value.length}</strong> days</p>
        <p class="range">{fmt.format(value[0]!)} &ndash; {fmt.format(value[value.length - 1]!)}</p>
      {/if}
      <p class="hint">Dots mark <em>important</em> dates (hover the 14th / 25th for a tooltip). Strikethrough marks restricted days. Scroll over the grid or use the arrows to change months - watch the transition.</p>
    </aside>
  </div>
</div>

<style>
  .wrap { padding: 20px; max-width: 900px; display: flex; flex-direction: column; gap: 16px; }
  header h2 { margin: 0 0 4px; font-size: 20px; font-weight: 700; }
  header p { margin: 0; color: var(--sg-muted, #64748b); font-size: 13.5px; line-height: 1.5; max-width: 640px; }
  code { background: var(--sg-row-hover-bg, #eef2ff); padding: 1px 5px; border-radius: 5px; font-size: 12px; }
  .controls { display: flex; flex-wrap: wrap; gap: 14px; align-items: end; }
  .controls label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; font-weight: 600; color: var(--sg-muted, #64748b); }
  .controls label.chk { flex-direction: row; align-items: center; gap: 6px; }
  .controls select { padding: 5px 8px; border: 1px solid var(--sg-border, #cbd5e1); border-radius: 7px; background: var(--sg-input-bg, #fff); color: inherit; font: inherit; font-size: 13px; }
  .stage { display: flex; gap: 24px; align-items: flex-start; flex-wrap: wrap; }
  .readout { flex: 1; min-width: 200px; }
  .readout h3 { margin: 0 0 8px; font-size: 13px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--sg-muted, #64748b); }
  .readout ul { margin: 0; padding-left: 18px; }
  .readout li { font-size: 14px; margin-bottom: 2px; }
  .readout .empty { color: var(--sg-muted, #94a3b8); font-style: italic; }
  .readout .range { font-size: 13px; color: var(--sg-muted, #64748b); }
  .readout .hint { margin-top: 14px; font-size: 12px; color: var(--sg-muted, #94a3b8); line-height: 1.5; }
</style>

View this example on GitHub

More Date & Time examples

  • Time picker - SvTimePicker: an analog clock-dial picker with 12/24-hour, minute snapping and hour to minute auto-switch. Drag the hand or click a number. The SvGrid time cell editor, usable standalone in any SvGrid app.
  • Date-time picker - SvDateTimePicker: a formatted input plus a portalled dropdown with DATE / TIME tabs (calendar + clock). Type a masked value or pick it; invalid input reverts. min/max, nullable, 12/24-hour, spin buttons. The SvGrid datetime cell editor, standalone in any SvGrid app.
  • Date-range input - SvDateRangeInput: a compact start/end field that opens a two-month range calendar with one-click presets (Today, Last 7 days, ...). Composes the same headless range engine as SvCalendar, and carries the shared editor contract - label, hint, error validation, dir (RTL) and localizable messages.
  • Calendar - range + presets - SvCalendar as a date-range picker: selectionMode="range" with a one-click presets rail (Today, Last 7 days, This month, Year to date...), animated month navigation and mouse-wheel scrolling. Presets resolve relative to today. Same component, no extra dependency.
  • Calendar - date of birth - SvCalendar as a date-of-birth picker: single selection with max clamped to today so future dates are blocked, opening on the decade (year) grid for fewer clicks to a birth year, with a live age readout. Same component SvGrid mounts to edit a date cell.