Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

svelte-multiselect

janosh54.3kMIT11.2.2TypeScript support: included

Svelte multi-select component

svelte, multi-select, single-select, form, input

readme

Svelte MultiSelect
 Svelte MultiSelect

Tests GitHub Pages NPM version Needs Svelte version REPL Open in StackBlitz

Keyboard-friendly, accessible and highly customizable multi-select component. View the docs

<slot name="examples" />

💡   Features

  • Bindable: bind:selected gives you an array of the currently selected options. Thanks to Svelte's 2-way binding, it can also control the component state externally through assignment selected = ['foo', 42].
  • Keyboard friendly for mouse-less form completion
  • No run-time deps: needs only Svelte as dev dependency
  • Dropdowns: scrollable lists for large numbers of options
  • Searchable: start typing to filter options
  • Tagging: selected options are listed as tags within the input
  • Single / multiple select: pass maxSelect={1, 2, 3, ...} prop to restrict the number of selectable options
  • Configurable: see props
<slot name="nav" />

🧪   Coverage

Statements Branches Lines
Statements Branches Lines

🔨   Installation

npm install --dev svelte-multiselect
pnpm add -D svelte-multiselect
yarn add --dev svelte-multiselect

📙   Usage

<script>
  import MultiSelect from 'svelte-multiselect'

  const ui_libs = [`Svelte`, `React`, `Vue`, `Angular`, `...`]

  let selected = $state([])
</script>

Favorite Frontend Tools?

<code>selected = {JSON.stringify(selected)}</code>

<MultiSelect bind:selected options={ui_libs} />

🧠   Mental Model

Prop Purpose Value
options What users can choose from Array of strings, numbers, or objects with label property
bind:selected Which options users have chosen Always an array: [], ['Apple'] or ['Apple', 'Banana']
bind:value Single-select convenience for the user-selected option Single item: 'Apple' (or null) if maxSelect={1}, otherwise same as selected

Common Patterns

<!-- Multi-select -->
<MultiSelect bind:selected options={['A', 'B', 'C']} />

<!-- Single-select -->
<MultiSelect bind:value options={colors} maxSelect={1} />

<!-- Object options (need 'label' property, can have arbitrary other keys, some like `value`, `disabled`, `preselected`, `style` have special meaning, see type ObjectOption) -->
<MultiSelect
  bind:selected
  options={[
    { label: 'Red', value: '#ff0000' },
    { label: 'Blue', value: '#0000ff' },
  ]}
/>

Troubleshooting

  • Object options not working? → Add label property
  • Dropdown not showing? → Check you have options and not disabled={true}
  • Want single item not array? → Use bind:value with maxSelect={1}
  • Types confusing? → Component auto-infers type of selected and value from your options array

🔣   Props

Complete reference of all props. Props are organized by importance - Essential Props are what you'll use most often.

💡 Tip: The Option type is automatically inferred from your options array, or you can import it: import { type Option } from 'svelte-multiselect'

Essential Props

These are the core props you'll use in most cases:

  1. options: Option[]  // REQUIRED

    The only required prop. Array of strings, numbers, or objects that users can select from. Objects must have a label property that will be displayed in the dropdown.

    <!-- Simple options -->
    <MultiSelect options={['Red', 'Green', 'Blue']} />
    
    <!-- Object options -->
    <MultiSelect
      options={[
        { label: 'Red', value: '#ff0000', hex: true },
        { label: 'Green', value: '#00ff00', hex: true },
      ]}
    />
  2. selected: Option[] = []  // bindable

    Your main state variable. Array of currently selected options. Use bind:selected for two-way binding.

    <script>
      let selected = $state(['Red']) // Pre-select Red
    </script>
    <MultiSelect bind:selected options={colors} />
  3. value: Option | Option[] | null = null  // bindable

    Alternative to selected. When maxSelect={1}, value is the single selected item (not an array). Otherwise, value equals selected.

    <!-- Single-select: value = 'Red' (not ['Red']) -->
    <MultiSelect bind:value options={colors} maxSelect={1} />
    
    <!-- Multi-select: value = ['Red', 'Blue'] (same as selected) -->
    <MultiSelect bind:value options={colors} />
  4. maxSelect: number | null = null

    Controls selection behavior. null = unlimited, 1 = single select, 2+ = limited multi-select.

    <!-- Unlimited selection -->
    <MultiSelect options={colors} />
    
    <!-- Single selection -->
    <MultiSelect options={colors} maxSelect={1} />
    
    <!-- Max 3 selections -->
    <MultiSelect options={colors} maxSelect={3} />
  5. placeholder: string | null = null

    Text shown when no options are selected.

  6. disabled: boolean = false

    Disables the component. Users can't interact with it, but it's still rendered.

  7. required: boolean | number = false

    For form validation. true means at least 1 option required, numbers specify exact minimum.

Commonly Used Props

  1. searchText: string = `` // bindable

    The text user entered to filter options. Bindable for external control.

  2. open: boolean = false // bindable

    Whether the dropdown is visible. Bindable for external control.

  3. allowUserOptions: boolean | `append` = false

    Whether users can create new options by typing. true = add to selected only, 'append' = add to both options and selected.

  4. allowEmpty: boolean = false

    Whether to allow the component to exist with no options. If false, shows console error when no options provided (unless loading, disabled, or allowUserOptions is true).

  5. loading: boolean = false

    Shows a loading spinner. Useful when fetching options asynchronously.

  6. invalid: boolean = false // bindable

    Marks the component as invalid (adds CSS class). Automatically set during form validation.

Advanced Props

  1. activeIndex: number | null = null  // bindable

    Zero-based index of currently active option in the filtered list.

  2. activeOption: Option | null = null  // bindable

    Currently active option (hovered or navigated to with arrow keys).

  3. createOptionMsg: string | null = `Create this option...`

    Message shown when allowUserOptions is enabled and user can create a new option.

  4. duplicates: boolean = false

    Whether to allow selecting the same option multiple times.

  1. filterFunc: (opt: Option, searchText: string) => boolean

    Custom function to filter options based on search text. Default filters by label.

  1. key: (opt: Option) => unknown

    Function to determine option equality. Default compares by lowercased label.

  2. closeDropdownOnSelect: boolean | 'if-mobile' | 'retain-focus' = 'if-mobile'

    Whether to close dropdown after selection. 'if-mobile' closes dropdown on mobile devices only (responsive). 'retain-focus' closes dropdown but keeps input focused for rapid typing to create custom options from text input (see allowUserOptions).

  3. resetFilterOnAdd: boolean = true

    Whether to clear search text when an option is selected.

  4. sortSelected: boolean | ((a: Option, b: Option) => number) = false

    Whether/how to sort selected options. true uses default sort, function enables custom sorting.

  5. portal: { target_node?: HTMLElement; active?: boolean } = {}

    Configuration for portal rendering. When active: true, the dropdown is rendered at document.body level with fixed positioning. Useful for avoiding z-index and overflow issues.

Form & Accessibility Props

  1. id: string | null = null

    Applied to the <input> for associating with <label> elements.

  2. name: string | null = null

    Form field name for form submission.

  3. autocomplete: string = 'off'

    Browser autocomplete behavior. Usually 'on' or 'off'.

  4. inputmode: string | null = null

    Hint for mobile keyboard type ('numeric', 'tel', 'email', etc.). Set to 'none' to hide keyboard.

  5. pattern: string | null = null

    Regex pattern for input validation.

UI & Behavior Props

  1. maxOptions: number | undefined = undefined

    Limit number of options shown in dropdown. undefined = no limit.

  2. minSelect: number | null = null

    Minimum selections required before remove buttons appear.

  3. autoScroll: boolean = true

    Whether to keep active option in view when navigating with arrow keys.

  4. breakpoint: number = 800

    Screen width (px) that separates 'mobile' from 'desktop' behavior.

  5. highlightMatches: boolean = true

    Whether to highlight matching text in dropdown options.

  6. parseLabelsAsHtml: boolean = false

    Whether to render option labels as HTML. Warning: Don't combine with allowUserOptions (XSS risk).

  7. selectedOptionsDraggable: boolean = !sortSelected

    Whether selected options can be reordered by dragging.

Message Props

  1. noMatchingOptionsMsg: string = 'No matching options'

    Message when search yields no results.

  2. duplicateOptionMsg: string = 'This option is already selected'

    Message when user tries to create duplicate option.

  3. defaultDisabledTitle: string = 'This option is disabled'

    Tooltip for disabled options.

  4. disabledInputTitle: string = 'This input is disabled'

    Tooltip when component is disabled.

  5. removeAllTitle: string = 'Remove all'

    Tooltip for remove-all button.

  6. removeBtnTitle: string = 'Remove'

    Tooltip for individual remove buttons.

  1. maxSelectMsg: ((current: number, max: number) => string) | null

    Function to generate "X of Y selected" message. null = no message.

DOM Element References (bindable)

These give you access to DOM elements after the component mounts:

  1. input: HTMLInputElement | null = null  // bindable

    Handle to the main <input> DOM element.

  2. form_input: HTMLInputElement | null = null  // bindable

    Handle to the hidden form input used for validation.

  3. outerDiv: HTMLDivElement | null = null  // bindable

    Handle to the outer wrapper <div> element.

Styling Props

For custom styling with CSS frameworks or one-off styles:

  1. style: string | null = null

    CSS rules for the outer wrapper div.

  2. inputStyle: string | null = null

    CSS rules for the main input element.

  3. ulSelectedStyle: string | null = null

    CSS rules for the selected options list.

  4. ulOptionsStyle: string | null = null

    CSS rules for the dropdown options list.

  5. liSelectedStyle: string | null = null

    CSS rules for selected option list items.

  6. liOptionStyle: string | null = null

    CSS rules for dropdown option list items.

CSS Class Props

For use with CSS frameworks like Tailwind:

  1. outerDivClass: string = ''

    CSS class for outer wrapper div.

  2. inputClass: string = ''

    CSS class for main input element.

  3. ulSelectedClass: string = ''

    CSS class for selected options list.

  4. ulOptionsClass: string = ''

    CSS class for dropdown options list.

  5. liSelectedClass: string = ''

    CSS class for selected option items.

  6. liOptionClass: string = ''

    CSS class for dropdown option items.

  7. liActiveOptionClass: string = ''

    CSS class for the currently active dropdown option.

  8. liUserMsgClass: string = ''

    CSS class for user messages (no matches, create option, etc.).

  9. liActiveUserMsgClass: string = ''

    CSS class for active user messages.

  10. maxSelectMsgClass: string = ''

    CSS class for the "X of Y selected" message.

Read-only Props (bindable)

These reflect internal component state:

  1. matchingOptions: Option[] = []  // bindable

    Currently filtered options based on search text.

Bindable Props

selected, value, searchText, open, activeIndex, activeOption, invalid, input, outerDiv, form_input, options, matchingOptions

🎰   Snippets

MultiSelect.svelte accepts the following named snippets:

  1. #snippet option({ option, idx }): Customize rendering of dropdown options. Receives as props an option and the zero-indexed position (idx) it has in the dropdown.
  2. #snippet selectedItem({ option, idx }): Customize rendering of selected items. Receives as props an option and the zero-indexed position (idx) it has in the list of selected items.
  3. #snippet spinner(): Custom spinner component to display when in loading state. Receives no props.
  4. #snippet disabledIcon(): Custom icon to display inside the input when in disabled state. Receives no props. Use an empty {#snippet disabledIcon()}{/snippet} to remove the default disabled icon.
  5. #snippet expandIcon(): Allows setting a custom icon to indicate to users that the Multiselect text input field is expandable into a dropdown list. Receives prop open: boolean which is true if the Multiselect dropdown is visible and false if it's hidden.
  6. #snippet removeIcon(): Custom icon to display as remove button. Will be used both by buttons to remove individual selected options and the 'remove all' button that clears all options at once. Receives no props.
  7. #snippet userMsg({ searchText, msgType, msg }): Displayed like a dropdown item when the list is empty and user is allowed to create custom options based on text input (or if the user's text input clashes with an existing option). Receives props:
    • searchText: The text user typed into search input.
    • msgType: false | 'create' | 'dupe' | 'no-match': 'dupe' means user input is a duplicate of an existing option. 'create' means user is allowed to convert their input into a new option not previously in the dropdown. 'no-match' means user input doesn't match any dropdown items and users are not allowed to create new options. false means none of the above.
    • msg: Will be duplicateOptionMsg or createOptionMsg (see props) based on whether user input is a duplicate or can be created as new option. Note this snippet replaces the default UI for displaying these messages so the snippet needs to render them instead (unless purposely not showing a message).
  8. snippet='after-input': Placed after the search input. For arbitrary content like icons or temporary messages. Receives props selected: Option[], disabled: boolean, invalid: boolean, id: string | null, placeholder: string, open: boolean, required: boolean. Can serve as a more dynamic, more customizable alternative to the placeholder prop.

Example using several snippets:

<MultiSelect options={[`Red`, `Green`, `Blue`, `Yellow`, `Purple`]}>
  {#snippet children({ idx, option })}
    <span style="display: flex; align-items: center; gap: 6pt">
      <span
        style:background={`${option}`}
        style="border-radius: 50%; width: 1em; height: 1em"
      ></span>
      {idx + 1}
      {option}
    </span>
  {/snippet}
  {#snippet spinner()}
    <CustomSpinner />
  {/snippet}
  {#snippet removeIcon()}
    <strong>X</strong>
  {/snippet}
</MultiSelect>

🎬   Events

MultiSelect.svelte dispatches the following events:

  1. onadd={(event) => console.log(event.detail.option)}

    Triggers when a new option is selected. The newly selected option is provided as event.detail.option.

  2. oncreate={(event) => console.log(event.detail.option)}

    Triggers when a user creates a new option (when allowUserOptions is enabled). The created option is provided as event.detail.option.

  3. onremove={(event) => console.log(event.detail.option)}`

    Triggers when a single selected option is removed. The removed option is provided as event.detail.option.

  4. onremoveAll={(event) => console.log(event.detail.options)}`

    Triggers when all selected options are removed. The payload event.detail.options gives the options that were previously selected.

  5. onchange={(event) => console.log(`${event.detail.type}: '${event.detail.option}'`)}

    Triggers when an option is either added (selected) or removed from selected, or all selected options are removed at once. type is one of 'add' | 'remove' | 'removeAll' and payload will be option: Option or options: Option[], respectively.

  6. onopen={(event) => console.log(`Multiselect dropdown was opened by ${event}`)}

    Triggers when the dropdown list of options appears. Event is the DOM's FocusEvent,KeyboardEvent or ClickEvent that initiated this Svelte dispatch event.

  7. onclose={(event) => console.log(`Multiselect dropdown was closed by ${event}`)}

    Triggers when the dropdown list of options disappears. Event is the DOM's FocusEvent, KeyboardEvent or ClickEvent that initiated this Svelte dispatch event.

For example, here's how you might annoy your users with an alert every time one or more options are added or removed:

<MultiSelect
  onchange={(e) => {
    if (e.detail.type === 'add') alert(`You added ${e.detail.option}`)
    if (e.detail.type === 'remove') alert(`You removed ${e.detail.option}`)
    if (e.detail.type === 'removeAll') alert(`You removed ${e.detail.options}`)
  }}
/>

Note: Depending on the data passed to the component the options(s) payload will either be objects or simple strings/numbers.

The above list of events are Svelte dispatch events. This component also forwards many DOM events from the <input> node: blur, change, click, keydown, keyup, mousedown, mouseenter, mouseleave, touchcancel, touchend, touchmove, touchstart. Registering listeners for these events works the same:

<MultiSelect
  options={[1, 2, 3]}
  onkeyup={(event) => console.log('key', event.target.value)}
/>

🦺   TypeScript

The type of options is inferred automatically from the data you pass. E.g.

const options = [
   { label: `foo`, value: 42 }
   { label: `bar`, value: 69 }
]
// type Option = { label: string, value: number }
const options = [`foo`, `bar`]
// type Option = string
const options = [42, 69]
// type Option = number

The inferred type of Option is used to enforce type-safety on derived props like selected as well as snippets. E.g. you'll get an error when trying to use a snippet that expects a string if your options are objects (see this comment for example screenshots).

You can also import the types this component uses for downstream applications:

import {
  DispatchEvents,
  MultiSelectEvents,
  ObjectOption,
  Option,
} from 'svelte-multiselect'

✨   Styling

There are 3 ways to style this component. To understand which options do what, it helps to keep in mind this simplified DOM structure of the component:

<div class="multiselect">
  <ul class="selected">
    <li>Selected 1</li>
    <li>Selected 2</li>
  </ul>
  <ul class="options">
    <li>Option 1</li>
    <li>Option 2</li>
  </ul>
</div>

With CSS variables

If you only want to make small adjustments, you can pass the following CSS variables directly to the component as props or define them in a :global() CSS context. See app.css for how these variables are set on the demo site of this component.

Minimal example that changes the background color of the options dropdown:

<MultiSelect --sms-options-bg="white" />
  • div.multiselect
    • border: var(--sms-border, 1pt solid lightgray): Change this to e.g. to 1px solid red to indicate this form field is in an invalid state.
    • border-radius: var(--sms-border-radius, 3pt)
    • padding: var(--sms-padding, 0 3pt)
    • background: var(--sms-bg)
    • color: var(--sms-text-color)
    • min-height: var(--sms-min-height, 22pt)
    • width: var(--sms-width)
    • max-width: var(--sms-max-width)
    • margin: var(--sms-margin)
    • font-size: var(--sms-font-size, inherit)
  • div.multiselect.open
    • z-index: var(--sms-open-z-index, 4): Increase this if needed to ensure the dropdown list is displayed atop all other page elements.
  • div.multiselect:focus-within
    • border: var(--sms-focus-border, 1pt solid var(--sms-active-color, cornflowerblue)): Border when component has focus. Defaults to --sms-active-color which in turn defaults to cornflowerblue.
  • div.multiselect.disabled
    • background: var(--sms-disabled-bg, lightgray): Background when in disabled state.
  • div.multiselect input::placeholder
    • color: var(--sms-placeholder-color)
    • opacity: var(--sms-placeholder-opacity)
  • div.multiselect > ul.selected > li
    • background: var(--sms-selected-bg, rgba(0, 0, 0, 0.15)): Background of selected options.
    • padding: var(--sms-selected-li-padding, 1pt 5pt): Height of selected options.
    • color: var(--sms-selected-text-color, var(--sms-text-color)): Text color for selected options.
  • ul.selected > li button:hover, button.remove-all:hover, button:focus
    • color: var(--sms-remove-btn-hover-color, lightskyblue): Color of the remove-icon buttons for removing all or individual selected options when in :focus or :hover state.
    • background: var(--sms-remove-btn-hover-bg, rgba(0, 0, 0, 0.2)): Background for hovered remove buttons.
  • div.multiselect > ul.options
    • background: var(--sms-options-bg, white): Background of dropdown list.
    • max-height: var(--sms-options-max-height, 50vh): Maximum height of options dropdown.
    • overscroll-behavior: var(--sms-options-overscroll, none): Whether scroll events bubble to parent elements when reaching the top/bottom of the options dropdown. See MDN.
    • z-index: var(--sms-options-z-index, 3): Z-index for the dropdown options list.
    • box-shadow: var(--sms-options-shadow, 0 0 14pt -8pt black): Box shadow of dropdown list.
    • border: var(--sms-options-border)
    • border-width: var(--sms-options-border-width)
    • border-radius: var(--sms-options-border-radius, 1ex)
    • padding: var(--sms-options-padding)
    • margin: var(--sms-options-margin, inherit)
  • div.multiselect > ul.options > li
    • scroll-margin: var(--sms-options-scroll-margin, 100px): Top/bottom margin to keep between dropdown list items and top/bottom screen edge when auto-scrolling list to keep items in view.
  • div.multiselect > ul.options > li.selected
    • background: var(--sms-li-selected-bg): Background of selected list items in options pane.
    • color: var(--sms-li-selected-color): Text color of selected list items in options pane.
  • div.multiselect > ul.options > li.active
    • background: var(--sms-li-active-bg, var(--sms-active-color, rgba(0, 0, 0, 0.15))): Background of active options. Options in the dropdown list become active either by mouseover or by navigating to them with arrow keys. Selected options become active when selectedOptionsDraggable=true and an option is being dragged to a new position. Note the active option in that case is not the dragged option but the option under it whose place it will take on drag end.
  • div.multiselect > ul.options > li.disabled
    • background: var(--sms-li-disabled-bg, #f5f5f6): Background of disabled options in the dropdown list.
    • color: var(--sms-li-disabled-text, #b8b8b8): Text color of disabled option in the dropdown list.
  • ::highlight(sms-search-matches): applies to search results in dropdown list that match the current search query if highlightMatches=true. These styles cannot be set via CSS variables. Instead, use a new rule set. For example:

    ::highlight(sms-search-matches) {
      color: orange;
      background: rgba(0, 0, 0, 0.15);
      text-decoration: underline;
    }

With CSS frameworks

The second method allows you to pass in custom classes to the important DOM elements of this component to target them with frameworks like Tailwind CSS.

  • outerDivClass: wrapper div enclosing the whole component
  • ulSelectedClass: list of selected options
  • liSelectedClass: selected list items
  • ulOptionsClass: available options listed in the dropdown when component is in open state
  • liOptionClass: list items selectable from dropdown list
  • liActiveOptionClass: the currently active dropdown list item (i.e. hovered or navigated to with arrow keys)
  • liUserMsgClass: user message (last child of dropdown list when no options match user input)
  • liActiveUserMsgClass: user message when active (i.e. hovered or navigated to with arrow keys)
  • maxSelectMsgClass: small span towards the right end of the input field displaying to the user how many of the allowed number of options they've already selected

This simplified version of the DOM structure of the component shows where these classes are inserted:

<div class="multiselect {outerDivClass}">
  <input class={inputClass} />
  <ul class="selected {ulSelectedClass}">
    <li class={liSelectedClass}>Selected 1</li>
    <li class={liSelectedClass}>Selected 2</li>
  </ul>
  <span class="maxSelectMsgClass">2/5 selected</span>
  <ul class="options {ulOptionsClass}">
    <li class={liOptionClass}>Option 1</li>
    <li class="{liOptionClass} {liActiveOptionClass}">
      Option 2 (currently active)
    </li>
    ...
    <li class="{liUserMsgClass} {liActiveUserMsgClass}">
      Create this option...
    </li>
  </ul>
</div>

With global CSS

Odd as it may seem, you get the most fine-grained control over the styling of every part of this component by using the following :global() CSS selectors. ul.selected is the list of currently selected options rendered inside the component's input whereas ul.options is the list of available options that slides out when the component is in its open state. See also simplified DOM structure.

:global(div.multiselect) {
  /* top-level wrapper div */
}
:global(div.multiselect.open) {
  /* top-level wrapper div when dropdown open */
}
:global(div.multiselect.disabled) {
  /* top-level wrapper div when in disabled state */
}
:global(div.multiselect > ul.selected) {
  /* selected list */
}
:global(div.multiselect > ul.selected > li) {
  /* selected list items */
}
:global(div.multiselect button) {
  /* target all buttons in this component */
}
:global(div.multiselect > ul.selected > li button, button.remove-all) {
  /* buttons to remove a single or all selected options at once */
}
:global(div.multiselect > input[autocomplete]) {
  /* input inside the top-level wrapper div */
}
:global(div.multiselect > ul.options) {
  /* dropdown options */
}
:global(div.multiselect > ul.options > li) {
  /* dropdown list items */
}
:global(div.multiselect > ul.options > li.selected) {
  /* selected options in the dropdown list */
}
:global(div.multiselect > ul.options > li:not(.selected):hover) {
  /* unselected but hovered options in the dropdown list */
}
:global(div.multiselect > ul.options > li.active) {
  /* active means item was navigated to with up/down arrow keys */
  /* ready to be selected by pressing enter */
}
:global(div.multiselect > ul.options > li.disabled) {
  /* options with disabled key set to true (see props above) */
}

🆕   Changelog

View the changelog.

🙏   Contributing

Here are some steps to get you started if you'd like to contribute to this project!

changelog

Changelog

v11.2.2

12 July 2025

Full Changelog: https://github.com/janosh/svelte-multiselect/compare/v11.2.1...v11.2.2

All notable changes to this project will be documented in this file. Dates are displayed in UTC.

v11.2.1

11 July 2025

Full Changelog: https://github.com/janosh/svelte-multiselect/compare/v11.2.0...v11.2.1

v11.2.0

11 July 2025

Full Changelog: https://github.com/janosh/svelte-multiselect/compare/v11.1.1...v11.2.0

v11.1.1

20 May 2025

  • more playwright tests for active+inactive portal 66ebd8d
  • pnpm remove svelte-multiselect bc6ee6c
  • replace browser from @app/env import with typeof window check in portal() action to support non-sveltekit apps 2a8f116
  • remove unused link-check-config.json c4f26ef

v11.1.0

17 May 2025

  • Multiselect portal #306
  • Multiselect portal (#306) #301

v11.0.0

15 May 2025

  • Prevent empty style attributes on ul.selected &gt; li and ul.options &gt; li #304
  • Breaking: Svelte 5 #295
  • update and fix linting, fix 2 dead readme links 91f22d0

v11.0.0-rc.1

24 October 2024

  • update MultiSelect.svelte for Svelte5 compatibility #293

v10.3.0

8 April 2024

  • MultiSelect fix form validation not resetting when required prop changes #286
  • Update readme.md #283
  • Housekeeping #282
  • Improve docs around Events #280
  • Add props style, inputStyle, liOptionStyle, liSelectedStyle, ulSelectedStyle, ulOptionsStyle #279
  • Add props liUserMsgClass and liUserMsgActiveClass #274
  • link check treat 403 http status as alive, update deps 1b04c15

v10.2.0

21 September 2023

  • Breaking: rename focusInputOnSelect to closeDropdownOnSelect #267
  • Fix empty event.detail.options payload on removeAll #266
  • update deps 58cee43

v10.1.0

18 July 2023

  • Always show createOptionMsg if allowUserOptions is truthy and user entered text #254
  • Add key style to ObjectOption for per-option inline CSS #252
  • Make selected and value each reactive to each other #250
  • [pre-commit.ci] pre-commit autoupdate #246

v10.0.0

1 July 2023

  • v10.0.0 #245
  • Add Multiselect prop maxOptions: number #243
  • Add &lt;slot name="user-msg"&gt; #240
  • Rename prop duplicateFunc() to key() #238
  • declare types #236
  • add <slot name="after-input"> 0fe8e8d
  • add sentence on inputmode='none' use case for hiding on-screen keyboards to readme 466f0a1

v9.0.0

1 June 2023

  • Revert "Fix Svelte 3.57 a11y (#215)" #232
  • Add default slot #231
  • CmdPalette add prop close_keys 099e1ac
  • add copy buttons to all code fences 950dcf6

v8.6.2

15 May 2023

  • Remove circular import between MultiSelect and index.ts #230
  • Automatically add "bug" label to bug report issues #229
  • Allow createOptionMsg=null to suppress console error when allowUserOptions thruthy #227
  • Fix MultiSelect unable to deselect object options #226
  • Update pnpm instructions #224

v8.6.1

30 April 2023

  • Fix svelte-check errors #223
  • fix: hide dropdown when custom messages are empty #220
  • DRY workflows #218
  • fix: allow object options to share the same label #217
  • delete wait_for_animation_end() and fix tests to use playwright auto-waiting correctly 2493029
  • don't highlight noMatchingOptionsMsg <span> in sms-search-matches a1feca7

v8.6.0

19 March 2023

  • Fix Svelte 3.57 a11y #215
  • Add prop highlightMatches to MultiSelect #212
  • fix: add missing role attr for <li> #211
  • update deps, set TS moduleResolution=bundler a12835e

v8.5.0

9 March 2023

  • Fix Lighthouse a11y issues #210
  • clear selected and searchText first, then trigger removeAll and change events in remove_all() #208
  • add props style, span_style, open, dialog, input, placeholder to CmdPalette + pipe through all other props to MultiSelect 97e6815
  • fix /css-classes example not applying styles from external classes aa1e28e

v8.4.0

1 March 2023

  • Make first matching option active automatically on entering searchText #206
  • add src/lib/NavPalette.svelte invoked with cmd+k for keyboard-only site navigation e3f4ea9
  • rename NavPalette to CmdPalette and make it execute generic actions on item select b27cd80
  • update to svelte-package v2 8d3df3e
  • fix selected options having cursor: grab (to indicate drag and drop support to change order) even if only single option selected 3309e1e

v8.3.0

25 January 2023

  • Don't error on removing options that are in selected but not in options array #204
  • Add class 'remove' to buttons that remove selected options #202
  • Add prop allowEmpty: boolean = false #198
  • Support immutable Svelte compiler option #197
  • group demo routes e813e48
  • breaking: rename addOptionMsg to createOptionMsg f24e025

v8.2.4

8 January 2023

  • Coverage badges #190
  • feat: add type inference for the options prop #189
  • feat: add type inference for the options prop (#189) #78
  • merge ExampleCode.svelte with CollapsibleCode.svelte 56ff99b
  • pnpm add -D svelte-zoo to outsource some site components and icons f2a387c
  • restore reactive searchText block in loading example 846da66
  • fix bunch of TS errors, add playwright test for dragging selected options to reorder a483217
  • add update-coverage package.json script 1094f08
  • add vite alias $root to clean up package.json, readme|contributing|changelog.md imports c19cbe4
  • mv src/components src/site 3683ed7

v8.2.3

28 December 2022

  • add 'Open in StackBlitz' links to example code fences ac07557

v8.2.2

18 December 2022

  • Issue console warning if sortSelected && selectedOptionsDraggable #187
  • Add new slot named 'expand-icon' #186

v8.2.1

10 December 2022

  • Fix allowUserOptions preventing dropdown list navigation with up/down arrow keys #184
  • Mdsvexamples #182
  • Add changelog & contributing pages to site #181
  • tweak contributing.md and css-classes example 6f78033
  • fix build error b896d36
  • fix readme badge for gh-pages.yml status 906b560

v8.2.0

30 November 2022

  • Add changelog.md #180
  • Draggable selected options #178
  • Set &lt;base href="/svelte-multiselect" /&gt; if !dev && !prerendering #172
  • Publish docs to GitHub pages #170
  • Contributing docs plus issue and PR templates with StackBlitz repro starter #169
  • add missing about field to bug-report issue template (closes #171) #171
  • fix prop form_input: set default value null to make it optional b150fe0

v8.1.0

18 November 2022

  • Add minSelect prop #166
  • Add pnpm test to readme #168
  • Add class for maxSelectMsg #167
  • Allow required=1 | 2 | ... to set minimum number of selected options for form submission #161
  • Add minSelect prop (#166) #163 #163 #163
  • mv /max-select example to /min-max-select 9838db8

v8.0.4

15 November 2022

  • Form validation docs #159
  • Don't console.error about missing options if disabled=true #158

v8.0.3

15 November 2022

  • Test uncovered lines #157
  • Don't console.error about missing options if loading=true #156
  • Measure vitest coverage with c8 #155
  • increase --sms-min-height 19->22pt 5d0e081

v8.0.2

7 November 2022

  • Pass JSON.stringified selected options to form submission handlers #152
  • Link check CI and readme housekeeping #149
  • REPL links for landing page examples #148
  • Add Collapsible code blocks to usage examples #143
  • REPL links for landing page examples (#148) #144 #145 #146 #147

v8.0.1

30 October 2022

  • Revert SCSS preprocessing #141
  • Add unit tests for 2-/1-way binding of activeIndex and activeOption #139

v8.0.0

22 October 2022

  • Add new prop value #138
  • New prop resetFilterOnAdd #137
  • yarn to pnpm #134
  • Rename prop noOptionsMsg->noMatchingOptionsMsg #133
  • remove props selectedLabels and selectedValues ef6598e

v7.1.0

13 October 2022

  • Allow preventing duplicate options when allowUserOptions is thruthy #132

v7.0.2

8 October 2022

  • Fix TypeError: Cannot read properties of null (reading 'get_label') - take 2 #131
  • Fix selecting options with falsy labels (like 0) #130

v7.0.1

6 October 2022

  • Fix single select with arrow and enter keys #128
  • Add SCSS preprocessing #126
  • [pre-commit.ci] pre-commit autoupdate #124
  • more unit tests 1adbc99
  • test required but empty MultiSelect fails form validity check (i.e. causes unsubmittable form) and filled one passes it fd8b377

v7.0.0

3 October 2022

  • Make selected a single value (not a length-1 array) if maxSelect=1 #123
  • Fix TypeError: Cannot read properties of null (reading 'get_label') at MultiSelect.svelte:75 #122
  • add stopPropagation to keydown handler (closes #114) #114

v6.1.0

30 September 2022

  • Forward input DOM events #120
  • Props to manipulating inputmode and pattern attributes #116
  • docs: remove userInputAs prop reference #115
  • Fix top option not selectable with enter key #113

v6.0.3

20 September 2022

  • Fix using arrow keys to control active option in dropdown list #111
  • eslintrc set @typescript-eslint/no-inferrable-types: off c688773

v6.0.2

17 September 2022

  • Test readme docs on CSS variables #109
  • Fix selected array not being initialized to options with preselected=true #108

v6.0.1

13 September 2022

  • Better props docs and test #105
  • fix breaking change sveltekit:prefetch renamed to data-sveltekit-prefetch 65ddbb9
  • fix .svx demo routes fde53f1
  • revert from adapter-netlify to adapter-static 224144d

v6.0.0

3 September 2022

  • Better on mobile and better about which option is active #103
  • SvelteKit routes auto migration #101

v5.0.6

2 August 2022

  • Fix 'Cannot find module scroll-into-view-if-needed' #99

v5.0.5

2 August 2022

  • Add scroll-into-view-if-needed ponyfill #97

v5.0.4

17 July 2022

  • Convert E2E tests fromvitest to @playwright/test #95
  • Allow empty Multiselect #94
  • Add new slot 'remove-icon' #93
  • [pre-commit.ci] pre-commit autoupdate #92

v5.0.3

1 July 2022

  • Reset activeOption to null if not in matchingOptions #90

v5.0.2

27 June 2022

  • Replace li.scrollIntoViewIfNeeded() with li.scrollIntoView() #88
  • Add new prop parseLabelsAsHtml #84
  • try fix flaky test 'multiselect > can select and remove many options' 2b0c453
  • bump netlify node to v18, update readme + deps 586c724
  • remove plausible.js analytics cd4c9f6

v5.0.1

23 April 2022

  • Strongly typed custom events #79

v5.0.0

21 April 2022

  • v5 release #76
  • Work with string options as is, don't convert to objects internally #75
  • v5 release (#76) #57

v4.0.6

7 April 2022

  • Fix backspace deleting multiple selected options if identical labels #72
  • Several fixes for allowUserOptions #69
  • [pre-commit.ci] pre-commit autoupdate #70

v4.0.5

2 April 2022

  • Fix MultiSelect localStorage binding #66

v4.0.4

30 March 2022

  • Move examples to new src/routes/demos dir #63
  • make ToC position fixed (closes #64) #64
  • check for undefined (not falsy) value in rawOp processing (fixes #65) #65
  • LanguageSnippet change SVG icons src repo to vscode-icons for more coverage 92390e9
  • more preselected slots in Examples.svelte cd0a01a

v4.0.3

23 March 2022

  • Add aria-label to hidden .form-control input #62
  • Add aria-label to hidden .form-control input (#62) #58 #35
  • fix dropdown closing when clicking between list items (closes #61) #61
  • svelte.config.js add kit.prerender.default: true, mv src/{global,app}.d.ts 4a84913

v4.0.2

13 March 2022

  • Improve a11y #60
  • Convert tests to Playwright #59
  • Convert tests to Playwright (#59) #58
  • add and document prop invalid (closes #47) #47
  • set width (not height) on svg icons and as px (not em) so they don't shrink with fluid typography on mobile screens ba77f93

v4.0.1

5 March 2022

  • Rename readonly to disabled #55
  • CSS and UX tweaks #52
  • Readme document test runner config to avoid transpiling errors in downstream testing #54
  • More tests #51
  • Add vitest #50
  • Rename readonly to disabled (#55) #45
  • close options dropdown list on input blur (fixes #53) #53
  • CSS and UX tweaks (#52) #44 #44 #44
  • Readme document test runner config to avoid transpiling errors in downstream testing (#54) #48

v4.0.0

21 February 2022

  • Implement allowUserOptions, autoScroll and loading (closes #39) #41
  • define DispatchEvents type used to annotate createEventDispatcher() #32
  • add prop required to prevent form submission if no options selected (closes #42) #42
  • Implement allowUserOptions, autoScroll and loading (closes #39) (#41) #39 #39

v3.3.0

20 February 2022

  • by default, only show maxSelectMsg if maxSelect != null and > 1 (closes #37) #37
  • add CSS var --sms-options-shadow defaults to subtle black shadow around dropdown list (0 0 14pt -8pt black) (closes #36) #36
  • add prop liActiveOptionClass = '' (closes #35) #35
  • turn searchText = and showOptions = false into bindable props (closes #33) #33
  • document missing noOptionsMsg prop (closes #34) #34
  • ensure custom class names (outerDivClass, ulOptionsClass) come last (closes #38) #38
  • fix ToC scroll to heading (closes #31) #31
  • only show remove all btn when maxSelect !== 1 (for #37) 64cfd8a

v3.2.3

19 February 2022

  • Fixes for focus on click and wiggle on hitting maxSelect #30

v3.2.2

16 February 2022

  • Expose filter method #29
  • readme improve docs on css variables and granular control through :global() selectors (closes #27) #27

v3.2.1

7 February 2022

  • mv input outside ul.selected for better HTML semantics (closes #26) #26

v3.2.0

3 February 2022

  • apply id prop to <input> insted of outer div (closes #25) #25
  • add eslint commit hook + update deps 6ad44b8
  • v.3.2.0 71ff2d1
  • add readme badge to document minimum svelte version (for #24) 7d9fe5a

v3.1.1

25 January 2022

  • wiggle the maxSelect msg on hitting selection limit (closes #19) #19
  • readme better docs for CSS variables, rename slots {options,selected}Renderer -> render{options,selected} c8ab724

v3.1.0

22 January 2022

  • add selectedRenderer + optionRenderer named slots (closes #21) #21
  • docs site use unmodified readme with slot to insert examples, yarn add svelte-github-corner 1072691
  • readme add note on type exports for TS users, add error page that redirects to index dde76c8

v3.0.1

7 January 2022

  • favorite web framework show Confetti.svelte on:add Svelte 8d109ee
  • bump svelte@3.45.0 to silence warning: MultiSelect has unused export property 'defaultDisabledTitle' (sveltejs/svelte#6964) f80a7a6
  • update readme + svelte-toc@0.2.0 40013ba
  • [pre-commit.ci] pre-commit autoupdate 0d05864
  • iOS Safari prevent zoom into page on focus MultiSelect input 44f17be

v3.0.0

29 December 2021

  • ensure active option is scrolled into view if needed (closes #15), breaking change: renames tokens to options #15

v2.0.0

24 December 2021

  • Convert options from simple strings to objects #16
  • Add local to transition:fly #14
  • add onClickOutside action, used to replace input.on:blur() for hiding options (closes #18) #18
  • update deps fb90f93
  • more keyboard friendliness by showing remove button focus and triggering on space bar or enter key b87d22b
  • add plausible 0557c0f

v1.2.2

27 October 2021

  • set <input> width back to 1pt as it's only needed to tab into, focus and blur <MultiSelect> (closes #12) #12
  • update readme 45c7993

v1.2.1

21 October 2021

  • make internal CSS easily overridable (sveltejs/svelte#6859) d15a445

v1.2.0

12 October 2021

  • add src/lib/index.ts for package path export '.' (closes #11) #11

v1.1.13

12 October 2021

  • add src/lib/index.ts for package path export '.' (closes #11) #11

v1.1.12

11 October 2021

  • Add new prop disabledOptions #9
  • add pre-commit hooks dfb6399
  • [pre-commit.ci] pre-commit autoupdate b69425d

v1.1.11

3 September 2021

  • fix removeAll button not dispatching remove and change events (closes #7) #7
  • remove @tsconfig/svelte, update deps 9b2c231
  • add type=(add|remove) detail to 'change' event dispatch 8290458

v1.1.10

12 August 2021

  • add on:change event and document events in readme (closes #5) #5

v1.1.9

12 July 2021

v1.1.8

7 July 2021

  • turn hard-coded remove button titles into props c35162b
  • guard against selected being nullish, keep ul.options in the DOM even if showoptions is false to allow selecting in dev tools for styling b9bd576

v1.1.7

5 July 2021

  • add css classes as props for use with tailwind (closes #3) #3

v1.1.6

23 June 2021

  • fix: don't remove tags if search string is non-empty, open options on clicking selected tags (#2) 5ffed50
  • update svelte-toc to fix deploy d5279dd

v1.1.5

22 June 2021

  • convert to svelte-kit package 9db3cfb

v1.1.4

21 June 2021

  • fix setting initial value for selected, fix setting class 'selected' in single mode 16d11de

v1.1.3

20 June 2021

  • replace prop single with maxSelect to specify any number of selectable options, add class single to div.multiselect if maxSelect===1 (#2) 36e916f
  • add linked headings 2eedf9a

v1.1.2

28 May 2021

v1.1.1

25 May 2021

  • add GitHubCorner.svelte for link to repo e80a402
  • remove selected tokens with backspace c5d7495
  • add readme badges 992eaa4
  • demo site fix stripping start of readme for docs 107273d
  • add svelte-toc table of contents to demo site 36aa1c5

v1.1.0

9 May 2021

  • import readme on demo site instead of duplication c0e4924
  • remove ununsed example.svx 2138caa
  • rename package dir, improve readme 0150378

v1.0.1

8 May 2021

  • remove hidden input for storing currently selected as JSON 802a219

v1.0.0

7 May 2021