Awesome
<div align="center"> <img src="https://raw.githubusercontent.com/rob-balfre/svelte-select/master/svelte-select.png" alt="Svelte Select" width="150" /> <h1>Svelte Select</h1> </div> <div align="center"> <a href="https://npmjs.org/package/svelte-select"> <img src="https://badgen.now.sh/npm/v/svelte-select" alt="version" /> </a> <a href="https://npmjs.org/package/svelte-select"> <img src="https://badgen.now.sh/npm/dm/svelte-select" alt="downloads" /> </a> </div> <div align="center">A select/autocomplete/typeahead Svelte component.</div>Demos
๐ฅ Examples of every prop, event, slot and more ๐ฅ
๐ REPL: Show me everything ๐บ
Installation
npm install svelte-select
Svelte 5
I'm not currently using Svelte 5 in my day job or personal projects so might be a while before I tackle porting / upgrading to Svelte 5. Looking forward to it though, just need to find the time!
Upgrading Svelte Select
See migration guide if upgrading
Rollup and low/no-build setups
List position and floating is powered by floating-ui
, see their package-entry-points docs if you encounter build errors.
Props
Prop | Type | Default | Description |
---|---|---|---|
items | any[] | [] | Array of items available to display / filter |
value | any | null | Selected value(s) |
justValue | any | null | READ-ONLY Selected value(s) excluding container object |
itemId | string | value | Override default identifier |
label | string | label | Override default label |
id | string | null | id attr for input field |
filterText | string | '' | Text to filter items by |
placeholder | string | Please select | Placeholder text |
hideEmptyState | boolean | false | When no items hide list |
listOpen | boolean | false | Open/close list |
class | string | '' | container classes |
containerStyles | string | '' | Add inline styles to container |
clearable | boolean | true | Enable clearing of value(s) |
disabled | boolean | false | Disable select |
multiple | boolean | false | Enable multi-select |
searchable | boolean | true | If false search/filtering is disabled |
groupHeaderSelectable | boolean | false | Enable selectable group headers |
focused | boolean | false | Controls input focus |
listAutoWidth | boolean | true | If false will ignore width of select |
showChevron | boolean | false | Show chevron |
inputAttributes | object | {} | Pass in HTML attributes to Select's input |
placeholderAlwaysShow | boolean | false | When multiple placeholder text will always show |
loading | boolean | false | Shows loading-icon . loadOptions will override this |
listOffset | number | 5 | px space between select and list |
debounceWait | number | 300 | milliseconds debounce wait |
floatingConfig | object | {} | Floating UI Config |
hasError | boolean | false | If true sets error class and styles |
name | string | null | Name attribute of hidden input, helpful for form actions |
required | boolean | false | If Select is within a <form> will restrict form submission |
multiFullItemClearable | boolean | false | When multiple selected items will clear on click |
closeListOnChange | boolean | true | After on:change list will close |
clearFilterTextOnBlur | boolean | true | If false , filterText value is preserved on:blur |
Named slots
<Select>
<div slot="prepend" />
<div slot="selection" let:selection let:index /> <!-- index only available when multiple -->
<div slot="clear-icon" />
<div slot="multi-clear-icon" />
<div slot="loading-icon" />
<div slot="chevron-icon" />
<div slot="list-prepend" />
<div slot="list" let:filteredItems />
<div slot="list-append" />
<div slot="item" let:item let:index />
<div slot="input-hidden" let:value />
<div slot="required" let:value />
<!-- Remember you can also use `svelte:fragment` to avoid a container DOM element. -->
<svelte:fragment slot="empty" />
</Select>
Events
Event Name | Callback | Description |
---|---|---|
change | { detail } | fires when the user selects an option |
input | { detail } | fires when the value has been changed |
focus | { detail } | fires when select > input on:focus |
blur | { detail } | fires when select > input on:blur |
clear | { detail } | fires when clear is invoked or item is removed (by user) from multi select |
loaded | { options } | fires when loadOptions resolves |
error | { type, details } | fires when error is caught |
filter | { detail } | fires when listOpen: true and items are filtered |
hoverItem | { detail } | fires when hoverItemIndex changes |
Items
items
can be simple arrays or collections.
<script>
import Select from 'svelte-select';
let simple = ['one', 'two', 'three'];
let collection = [
{ value: 1, label: 'one' },
{ value: 2, label: 'two' },
{ value: 3, label: 'three' },
];
</script>
<Select items={simple} />
<Select items={collection} />
They can also be grouped and include non-selectable items.
<script>
import Select from 'svelte-select';
const items = [
{value: 'chocolate', label: 'Chocolate', group: 'Sweet'},
{value: 'pizza', label: 'Pizza', group: 'Savory'},
{value: 'cake', label: 'Cake', group: 'Sweet', selectable: false},
{value: 'chips', label: 'Chips', group: 'Savory'},
{value: 'ice-cream', label: 'Ice Cream', group: 'Sweet'}
];
const groupBy = (item) => item.group;
</script>
<Select {items} {groupBy} />
You can also use custom collections.
<script>
import Select from 'svelte-select';
const itemId = 'id';
const label = 'title';
const items = [
{id: 0, title: 'Foo'},
{id: 1, title: 'Bar'},
];
</script>
<Select {itemId} {label} {items} />
Async Items
To load items asynchronously then loadOptions
is the simplest solution. Supply a function that returns a Promise
that resolves with a list of items. loadOptions
has debounce baked in and fires each time filterText
is updated.
<script>
import Select from 'svelte-select';
import { someApiCall } from './services';
async function examplePromise(filterText) {
// Put your async code here...
// For example call an API using filterText as your search params
// When your API responds resolve your Promise
let res = await someApiCall(filterText);
return res;
}
</script>
<Select loadOptions={examplePromise} />
Advanced List Positioning / Floating
svelte-select
uses floating-ui to control the list floating. See their docs and pass in your config via the floatingConfig
prop.
<script>
import Select from 'svelte-select';
let floatingConfig = {
strategy: 'fixed'
}
</script>
<Select {floatingConfig} />
Exposed methods
These internal functions are exposed to override if needed. Look through the test file (test/src/index.js) for examples.
export let itemFilter = (label, filterText, option) => label.toLowerCase().includes(filterText.toLowerCase());
export let groupBy = undefined;
export let groupFilter = groups => groups;
export let createGroupHeaderItem = groupValue => {
return {
value: groupValue,
label: groupValue
};
};
export function handleClear() {
value = undefined;
listOpen = false;
dispatch("clear", value);
handleFocus();
}
export let loadOptions = undefined; // if used must return a Promise that updates 'items'
/* Return an object with { cancelled: true } to keep the loading state as active. */
export const getFilteredItems = () => {
return filteredItems;
};
export let debounce = (fn, wait = 1) => {
clearTimeout(timeout);
timeout = setTimeout(fn, wait);
};
Override core functionality at your own risk! See (get-items.js & filter.js)
// core replaceable methods...
<Select
filter={...}
getItems={...}
/>
A11y (Accessibility)
Override these methods to change the aria-context
and aria-selection
text.
export let ariaValues = (values) => {
return `Option ${values}, selected.`;
}
export let ariaListOpen = (label, count) => {
return `You are currently focused on option ${label}. There are ${count} results available.`;
}
export let ariaFocused = () => {
return `Select is focused, type to refine list, press down to open the menu.`;
}
CSS custom properties (variables)
You can style a component by overriding the available CSS custom properties.
<script>
import Select from 'svelte-select';
</script>
<Select --border-radius= "10px" --placeholder-color="blue" />
You can also use the inputStyles
prop to write in any override styles needed for the input.
<script>
import Select from 'svelte-select';
const items = ['One', 'Two', 'Three'];
</script>
<Select {items} inputStyles="box-sizing: border-box;"></Select>
๐งช Experimental: Replace styles (Tailwind, Bootstrap, Bulma etc)
If you'd like to supply your own styles use: import Select from 'svelte-select/no-styles/Select.svelte'
. Then somewhere in your code or build pipeline add your own. There is a tailwind stylesheet via import 'svelte-select/tailwind.css'
. It uses @extend
so PostCSS is required.