@referralgps/selectra
v1.0.10
Published
Selectra — A powerful, extensible <select> UI control for tagging, contact lists, country selectors, and autocomplete. Built with Alpine.js and Tailwind CSS.
Readme
Selectra
A powerful, extensible
<select>UI control — rebuilt with Alpine.js and Tailwind CSS.
Selectra is a modern rewrite of Selectize.js, designed for tagging, contact lists, country selectors, and autocomplete. It drops the jQuery dependency entirely in favor of Alpine.js reactivity and Tailwind CSS styling.
Why Selectra?
| | Selectize.js (legacy) | Selectra | |---|---|---| | UI Framework | jQuery | Alpine.js | | Styling | LESS / SCSS + Bootstrap themes | Tailwind CSS utilities | | Build Tool | Gulp | Vite | | Tests | Karma + Mocha | Vitest | | Bundle (JS) | ~88 KB min | ~25 KB min | | CSS | Multiple theme files | Single 21 KB utility file | | jQuery Required | Yes | No |
Features
- Single & Multi Select — auto-detected from
maxItemsor explicitmode - Tagging / Create — create new options on the fly with validation
- Fuzzy Search — built-in Sifter engine with diacritics support
- Remote Loading — fetch options from API with debounced requests
- Option Groups — grouped options with sticky headers
- Keyboard Navigation — arrows, enter, escape, tab, backspace, ctrl+A
- Copy / Paste — split pasted text into multiple items via delimiter
- Custom Rendering — templates for options, items, create prompt, no-results, loading
- Plugin System — 9 built-in plugins, easy to create custom ones
- Accessible — focus management, label association, keyboard-first
- RTL Support — auto-detected from CSS
direction - Tailwind CSS — fully styled with utilities, trivially customizable
- Selected Count Mode — show a count badge instead of tags in multi-select, with checkmarks on selected options
- Array-Format Options — pass
[text, value]tuples directly (e.g. from Railspluck) - Lightweight — ~25 KB gzipped JS, zero runtime dependencies beyond Alpine.js
Requirements
- Node.js v14 or newer (for npm/yarn install)
- Alpine.js v3.x (peer dependency)
- Tailwind CSS v2.x or v3.x (for styling, optional but recommended)
Installation
npm install @referralgps/selectra alpinejsImport and register the plugin in your JavaScript entry point:
import Alpine from 'alpinejs';
import Selectra from '@referralgps/selectra';
import '@referralgps/selectra/css';
Alpine.plugin(Selectra);
Alpine.start();Quick Start
Add x-data="selectra({...})" and x-selectra to any element. The template is rendered automatically by the x-selectra directive — no manual markup required.
Single Select
<div x-data="selectra({
mode: 'single',
placeholder: 'Select a country...',
options: [
{ value: 'us', text: 'United States' },
{ value: 'ca', text: 'Canada' },
{ value: 'mx', text: 'Mexico' },
]
})" x-selectra></div>Multi Select with Tags
<div x-data="selectra({
mode: 'multi',
placeholder: 'Select languages...',
create: true,
options: [
{ value: 'js', text: 'JavaScript' },
{ value: 'py', text: 'Python' },
{ value: 'go', text: 'Go' },
]
})" x-selectra></div>Multi Select with Count Badge
Show the number of selected items as a badge instead of individual tags. Selected options display a ✓ checkmark in the dropdown and can be toggled on/off by clicking.
<div x-data="selectra({
mode: 'multi',
showSelectedCount: true,
placeholder: 'Select countries...',
options: [
{ value: 'us', text: 'United States' },
{ value: 'ca', text: 'Canada' },
{ value: 'mx', text: 'Mexico' },
]
})" x-selectra></div>Form Field Name
Use the name option to auto-create a hidden <input> for form submission — no <select> element needed:
<form method="POST" action="/submit">
<div x-data="selectra({
mode: 'multi',
name: 'languages',
placeholder: 'Select languages...',
options: [
{ value: 'js', text: 'JavaScript' },
{ value: 'py', text: 'Python' },
{ value: 'go', text: 'Go' },
]
})" x-selectra></div>
<button type="submit">Submit</button>
</form>Array-Format Options
Pass options as [text, value] tuples — ideal for data from Rails pluck or similar:
<div x-data="selectra({
mode: 'single',
placeholder: 'Select provider...',
options: [
['Aetna Better Health', 121],
['Blue Cross Blue Shield', 205],
['Cigna Healthcare', 310],
]
})" x-selectra></div>Native <select> Enhancement
Enhance a standard <select> element — options are read automatically:
<div x-data="selectra()" x-selectra>
<select>
<option value="">Pick a fruit...</option>
<option value="apple">Apple</option>
<option value="banana">Banana</option>
<option value="cherry">Cherry</option>
</select>
</div>Configuration
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| mode | 'single' \| 'multi' | auto | Selection mode |
| options | Array | [] | Available options |
| items | Array | [] | Pre-selected values |
| maxItems | number \| null | null | Max selectable items |
| maxOptions | number | 1000 | Max dropdown options |
| create | boolean \| function | false | Allow creating new options |
| createOnBlur | boolean | false | Create item when field loses focus |
| createFilter | RegExp \| function | null | Filter for creatable values |
| placeholder | string | '' | Placeholder text |
| name | string \| null | null | Form field name — auto-creates hidden input when no source element exists |
| valueField | string | 'value' | Property for option value |
| labelField | string | 'text' | Property for display label |
| searchField | string[] | ['text'] | Fields to search |
| searchConjunction | 'and' \| 'or' | 'and' | Multi-term search logic |
| sortField | string \| Array | '$order' | Sort field(s) |
| highlight | boolean | true | Highlight matches |
| openOnFocus | boolean | true | Open dropdown on focus |
| selectOnTab | boolean | true | Tab selects active option |
| closeAfterSelect | boolean | false | Close dropdown after selection |
| hideSelected | boolean \| null | auto | Hide selected from dropdown |
| showSelectedCount | boolean | false | Show count badge instead of tags in multi-select |
| delimiter | string | ',' | Value separator |
| splitOn | RegExp \| string | null | Regex for splitting pasted values |
| diacritics | boolean | true | International character support |
| normalize | boolean | true | NFD normalize search queries |
| preload | boolean \| 'focus' | false | Preload options |
| load | function \| null | null | Remote data loader |
| loadThrottle | number | 300 | Load debounce delay (ms) |
| plugins | Array | [] | Plugins to activate |
| dropdownPlaceholder | string | '' | Pre-search placeholder
Custom Rendering
selectra({
render: {
option: (data, escape) => `<div class="flex items-center gap-2">
<img src="${data.avatar}" class="w-6 h-6 rounded-full">
<span>${escape(data.text)}</span>
</div>`,
item: (data, escape) => escape(data.text),
optionCreate: (data, escape) => `Add "${escape(data.input)}"`,
noResults: () => 'Nothing found',
loading: () => 'Searching...',
}
})Remote Loading
selectra({
load: (query, callback) => {
fetch(`/api/search?q=${encodeURIComponent(query)}`)
.then(res => res.json())
.then(data => callback(data.results))
.catch(() => callback([]));
},
loadThrottle: 300,
})API Methods
| Method | Description |
|--------|-------------|
| addOption(data) | Add one or more options |
| updateOption(value, data) | Update an existing option |
| removeOption(value) | Remove an option |
| clearOptions() | Remove all options (keeps selected) |
| getOption(value) | Get option data by value |
| addItem(value) | Select an item |
| removeItem(value) | Deselect an item |
| clear() | Clear all selections |
| getValue() | Get current value(s) |
| setValue(value) | Set selected value(s) |
| createItem(input?) | Create new item from input |
| open() | Open dropdown |
| close() | Close dropdown |
| focus() | Focus the control |
| blur() | Remove focus |
| lock() / unlock() | Temporarily disable input |
| disable() / enable() | Disable/enable control |
| setMaxItems(max) | Update max items limit |
Events
Events are dispatched as custom DOM events on the wrapper element:
el.addEventListener('selectra:change', (e) => {
console.log('Value changed:', e.detail);
});| Event | Detail |
|-------|--------|
| selectra:change | [value] |
| selectra:itemadd | [value, data] |
| selectra:itemremove | [value] |
| selectra:clear | [] |
| selectra:optionadd | [value, data] |
| selectra:optionremove | [value] |
| selectra:dropdownopen | [] |
| selectra:dropdownclose | [] |
| selectra:type | [query] |
| selectra:focus | [] |
| selectra:blur | [] |
| selectra:initialize | [] |
Plugins
Built-in Plugins
Activate plugins via the plugins config option:
selectra({
plugins: ['remove_button', 'clear_button', { name: 'tag_limit', options: { tagLimit: 3 } }]
})| Plugin | Description |
|--------|-------------|
| remove_button | Add × button to each selected tag |
| clear_button | Add a clear-all button to the control |
| restore_on_backspace | Restore deleted item text to input |
| dropdown_header | Add a header to the dropdown |
| tag_limit | Limit visible tags with "+N" badge |
| auto_select_on_type | Auto-select first match on blur |
| select_on_focus | Put current value into search on focus |
| read_only | Make component read-only |
| auto_position | Smart dropdown positioning (above/below) |
Custom Plugins
import { registerPlugin } from '@referralgps/selectra';
registerPlugin('my_plugin', function(options) {
// `this` is the selectra component instance
console.log('Plugin initialized with', options);
// Override methods
const originalOpen = this.open.bind(this);
this.open = () => {
console.log('Opening dropdown');
originalOpen();
};
});Building
npm install
npm run build # Build dist/
npm run dev # Dev server with HMR
npm run test # Run testsMigration from jQuery Version
| jQuery Selectize | Selectra |
|-----------------|---------------------|
| $('select').selectize({...}) | x-data="selectra({...})" |
| instance.addItem(val) | Direct method call in Alpine scope |
| $.fn.selectize plugin | Alpine.plugin(Selectra) |
| jQuery events | Custom DOM events |
| LESS/SCSS themes | Tailwind CSS utilities |
| Bootstrap themes | Use Tailwind directly |
License
Apache-2.0
