Page-Reload Persistent MultiSelect

This example shows how to combine MultiSelect with sessionStorage to persist the selected state across page reloads.

svelte<script lang="ts">
  import { MultiSelect } from 'svelte-widgets'
  const languages =
    `JavaScript TypeScript CoffeeScript Python Ruby C C# C++ Go Swift Java Rust Kotlin Haskell Scala Clojure Erlang Elixir F# Dart Elm Julia Lua R OCaml Perl PHP`
      .split(` `)
      .toSorted()
  import { onMount } from 'svelte'

  let selected: string[] = $state([])
  const default_languages = `Python TypeScript C Haskell`.split(` `)
  const is_language_array = (value: unknown): value is string[] =>
    Array.isArray(value) &&
    value.every((item) => typeof item === `string` && languages.includes(item))

  onMount(() => {
    try {
      const parsed: unknown = JSON.parse(sessionStorage.getItem(`languages`) ?? `null`)
      selected = is_language_array(parsed) ? parsed : default_languages
    } catch {
      selected = default_languages
    }
  })

  $effect(() => {
    if (sessionStorage) sessionStorage[`languages`] = JSON.stringify(selected)
  })
</script>

{#snippet language_option(option: string, idx?: number, style?: string)}
  {@const language = option.toLowerCase().replaceAll(`+`, `plus`).replace(`#`, `sharp`)}
  <span style={`display: inline-flex; align-items: center; gap: 5pt; ${style ?? ``}`}>
    {#if idx !== undefined}<strong>{idx + 1}</strong>{/if}
    <img
      src={`https://cdn.jsdelivr.net/gh/devicons/devicon/icons/${language}/${language}-original.svg`}
      alt={option}
      style={`height: 20px; ${option === `Rust` ? `filter: invert(1)` : ``}`}
      onerror={(event) => (event.currentTarget.hidden = true)}
    />
    {option}
  </span>
{/snippet}

<MultiSelect
  options={languages}
  placeholder="What languages do you know?"
  bind:value={selected}
>
  {#snippet children({ idx, option })}
    {@render language_option(option, idx)}
  {/snippet}
</MultiSelect>

Array Cloning Infinite Loop Prevention (#309)

Tests that binding to reactive wrappers (Svelte stores, Superforms, etc.) that clone arrays on assignment doesn’t cause infinite loops.

Modified: 1 times

svelte<script lang="ts">
  import { MultiSelect } from 'svelte-widgets'
  import { type Writable, writable } from 'svelte/store'

  const options: string[] = [`Red`, `Green`, `Blue`]
  let increment = $state(0)
  let list_store: Writable<string[]> = writable([])

  list_store.subscribe(() => increment++)
</script>

<MultiSelect {options} bind:value={$list_store} placeholder="Select colors..." />
<p id="store-binding-status">
  Modified: {increment} times
  {#if increment > 50}⚠️ Regression!{:else if increment > 1}✅ Fixed{/if}
</p>