svelte-multiselect Svelte MultiSelect

Selection History (Undo/Redo)

Selection history is enabled by default, allowing users to undo and redo their selections with Ctrl+Z / Cmd+Z and Ctrl+Shift+Z / Cmd+Shift+Z. This is useful for complex selection workflows where users might want to revert changes.

Basic Usage

History tracking works out of the box with a default max of 50 entries. Pass a number to customize the limit, or false/0 to disable.

Use Ctrl+Z to undo and Ctrl+Shift+Z to redo.

Selected: 0 items canUndo: false canRedo: false

Event Log

Undo/redo events will appear here

<script>
  import MultiSelect from '$lib'
  import { ColorSnippet } from '$site'
  import { colors } from '$site/options'

  let selected = $state([])
  let undo = $state()
  let redo = $state()
  let canUndo = $state(false)
  let canRedo = $state(false)
  let events = $state([])
  // Use same platform detection as component (userAgentData is modern API, userAgent is fallback)
  const is_mac = typeof navigator !== 'undefined' &&
    (navigator.userAgentData?.platform === 'macOS' ||
      /Mac|iPhone|iPad|iPod/.test(navigator.userAgent))
  const mod_key = is_mac ? 'Cmd' : 'Ctrl'

  function log_event(name, data) {
    events = [
      { name, data: JSON.stringify(data), time: new Date().toLocaleTimeString() },
      ...events.slice(0, 4),
    ]
  }
</script>

<div class="demo-grid" id="history-demo">
  <section class="controls" id="history-multiselect">
    <p class="tip">
      Use <kbd>{mod_key}+Z</kbd> to undo and <kbd>{mod_key}+Shift+Z</kbd> to redo.
    </p>

    <div class="button-group">
      <button id="undo-btn" onclick={() => undo?.()} disabled={!canUndo}>
        ↩ Undo
      </button>
      <button id="redo-btn" onclick={() => redo?.()} disabled={!canRedo}>
        Redo ↪
      </button>
    </div>

    <MultiSelect
      options={colors}
      history={true}
      placeholder="Select colors..."
      bind:selected
      bind:undo
      bind:redo
      bind:canUndo
      bind:canRedo
      onundo={(data) => log_event('onundo', data)}
      onredo={(data) => log_event('onredo', data)}
    >
      {#snippet children({ idx, option })}
        <ColorSnippet {idx} {option} />
      {/snippet}
    </MultiSelect>

    <p class="status">
      <span id="selection-count">Selected: {selected.length} item{
          selected.length !== 1 ? 's' : ''
        }</span>
      <span>canUndo: <strong id="can-undo-status">{canUndo}</strong></span>
      <span>canRedo: <strong id="can-redo-status">{canRedo}</strong></span>
    </p>
  </section>

  <section class="event-log" id="history-event-log">
    <h4>Event Log</h4>
    {#each events as entry}
      <div class="log-entry">
        <span class="event-name">{entry.name}</span>
        <span class="time">{entry.time}</span>
        <pre>{entry.data}</pre>
      </div>
    {:else}
      <p class="no-events">Undo/redo events will appear here</p>
    {/each}
  </section>
</div>

Props Reference

PropTypeDefaultDescription
historyboolean \| numbertrueEnable history (default). true = max 50 entries, number = custom limit, false = disabled
undo() => boolean-Bindable function to undo last change. Returns false if nothing to undo
redo() => boolean-Bindable function to redo last undone change. Returns false if nothing to redo
canUndobooleanfalseBindable state indicating if undo is available
canRedobooleanfalseBindable state indicating if redo is available

Events

EventDataDescription
onundo{ previous: T[], current: T[] }Fired after undo is executed
onredo{ previous: T[], current: T[] }Fired after redo is executed

Keyboard Shortcuts

History supports platform-aware keyboard shortcuts:

Custom shortcuts can be configured via the shortcuts prop:

<MultiSelect
  history={true}
  shortcuts={{ undo: 'alt+z', redo: 'alt+shift+z' }}
/>

Implementation Notes