Select
This library is a work-in-progress. We are releasing it early to gather feedback, but it is not ready for production.
The Select component is a lightly styled native HTML select element that provides consistent styling across browsers while maintaining native functionality and accessibility.
Examples
Live Preview
Basic Select
A standard select dropdown with custom styling.
Size Variants
The Select component comes in three sizes: small, default, and large.
Disabled State
Selects can be disabled to prevent user interaction.
Multiple Selection
Enable multiple selection mode with the multiple prop. Use Ctrl/Cmd + Click to select multiple options.
With Change Handler
Listen to selection changes with the @change event.
With v-model
Use v-model:value for two-way data binding.
Multiple Selection with v-model
Use v-model:value with multiple to manage an array of selected values.
Event Handling
The Select component fires multiple events: @change, @focus, @blur, and @click.
External Label Support
The Select component supports external labels with helper text, required fields, and validation states.
Label Positioning
Control label position with label-position: 'top' (default), 'start', 'end', or 'bottom'. Note: 'bottom' is not recommended as the dropdown will cover the label.
CSS Shadow Parts Customization
Use CSS Shadow Parts to customize the select's appearance.
View Vue Code
<template>
<section>
<div class="mbe4">
<h2>Basic Select</h2>
<p class="mbs2 mbe3">
A standard select dropdown with custom styling.
</p>
</div>
<div class="mbe4" style="max-width: 400px;">
<VueSelect
label="Greatest Tennis Player"
name="tennis"
>
<option value="">- Select a player -</option>
<option value="andre">Andre Agassi</option>
<option value="serena">Serena Williams</option>
<option value="roger">Roger Federer</option>
<option value="mac">John McEnroe</option>
<option value="martina">Martina Navratilova</option>
<option value="rafa">Rafael Nadal</option>
<option value="borg">Bjorn Borg</option>
<option value="althea">Althea Gibson</option>
</VueSelect>
</div>
<div class="mbe4">
<h2>Size Variants</h2>
<p class="mbs2 mbe3">
The Select component comes in three sizes: small, default, and large.
</p>
</div>
<div
class="mbe4"
style="max-width: 400px;"
>
<div class="mbe3">
<VueSelect
label="Small Select"
size="small"
name="small"
>
<option value="">Select an option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</VueSelect>
</div>
<div class="mbe3">
<VueSelect
label="Default Select"
name="default"
>
<option value="">Select an option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</VueSelect>
</div>
<div class="mbe3">
<VueSelect
label="Large Select"
size="large"
name="large"
>
<option value="">Select an option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</VueSelect>
</div>
</div>
<div class="mbe4">
<h2>Disabled State</h2>
<p class="mbs2 mbe3">
Selects can be disabled to prevent user interaction.
</p>
</div>
<div
class="mbe4"
style="max-width: 400px;"
>
<VueSelect
label="Disabled Select"
:disabled="true"
name="disabled"
>
<option value="">Cannot select</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</VueSelect>
</div>
<div class="mbe4">
<h2>Multiple Selection</h2>
<p class="mbs2 mbe3">
Enable multiple selection mode with the <code>multiple</code> prop. Use Ctrl/Cmd + Click to select multiple options.
</p>
</div>
<div
class="mbe4"
style="max-width: 400px;"
>
<VueSelect
label="Favorite Tennis Players (Select Multiple)"
:multiple="true"
:multiple-size="6"
name="multiple"
>
<option value="andre">Andre Agassi</option>
<option value="serena">Serena Williams</option>
<option value="roger">Roger Federer</option>
<option value="mac">John McEnroe</option>
<option value="martina">Martina Navratilova</option>
<option value="rafa">Rafael Nadal</option>
<option value="borg">Bjorn Borg</option>
<option value="althea">Althea Gibson</option>
</VueSelect>
</div>
<div class="mbe4">
<h2>With Change Handler</h2>
<p class="mbs2 mbe3">
Listen to selection changes with the <code>@change</code> event.
</p>
</div>
<div
class="mbe4"
style="max-width: 400px;"
>
<VueSelect
label="Select a Fruit"
name="fruit"
@change="handleChange"
>
<option value="">Choose a fruit</option>
<option value="apple">Apple</option>
<option value="banana">Banana</option>
<option value="orange">Orange</option>
<option value="grape">Grape</option>
</VueSelect>
<p
v-if="selectedFruit"
style="margin-top: 0.75rem; padding: 0.75rem; background: var(--ag-background-secondary); border-radius: var(--ag-radius-md);"
>
You selected: <strong>{{ selectedFruit }}</strong>
</p>
</div>
<div class="mbe4">
<h2>With v-model</h2>
<p class="mbs2 mbe3">
Use <code>v-model:value</code> for two-way data binding.
</p>
</div>
<div
class="mbe4"
style="max-width: 400px;"
>
<VueSelect
label="Choose Your Favorite Color"
name="color"
v-model:value="selectedColor"
>
<option value="">Choose a color</option>
<option value="red">Red</option>
<option value="blue">Blue</option>
<option value="green">Green</option>
<option value="purple">Purple</option>
</VueSelect>
<p
v-if="selectedColor"
style="margin-top: 0.75rem; padding: 0.75rem; background: var(--ag-background-secondary); border-radius: var(--ag-radius-md);"
>
Your favorite color is: <strong>{{ selectedColor }}</strong>
</p>
</div>
<div class="mbe4">
<h2>Multiple Selection with v-model</h2>
<p class="mbs2 mbe3">
Use <code>v-model:value</code> with <code>multiple</code> to manage an array of selected values.
</p>
</div>
<div
class="mbe4"
style="max-width: 400px;"
>
<VueSelect
label="Select Your Favorite Sports (Ctrl/Cmd + Click)"
name="sports"
v-model:value="selectedSports"
:multiple="true"
:multiple-size="5"
>
<option value="tennis">Tennis</option>
<option value="football">Football</option>
<option value="basketball">Basketball</option>
<option value="baseball">Baseball</option>
<option value="soccer">Soccer</option>
</VueSelect>
<p
v-if="selectedSports.length > 0"
style="margin-top: 0.75rem; padding: 0.75rem; background: var(--ag-background-secondary); border-radius: var(--ag-radius-md);"
>
You selected <strong>{{ selectedSports.length }}</strong> sport(s): {{ selectedSports.join(', ') }}
</p>
</div>
<div class="mbe4">
<h2>Event Handling</h2>
<p class="mbs2 mbe3">
The Select component fires multiple events: <code>@change</code>, <code>@focus</code>, <code>@blur</code>, and <code>@click</code>.
</p>
</div>
<div
class="mbe4"
style="max-width: 600px;"
>
<VueSelect
label="Interact with this select to see events"
name="events"
@change="handleEventChange"
@focus="handleFocus"
@blur="handleBlur"
@click="handleClick"
>
<option value="">Select an option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</VueSelect>
<div
v-if="eventLog.length > 0"
style="margin-top: 0.75rem; padding: 0.75rem; background: var(--ag-background-secondary); border-radius: var(--ag-radius-md);"
>
<strong>Event Log (most recent first):</strong>
<div style="margin-top: 0.5rem; font-family: monospace; font-size: 0.875rem; line-height: 1.6;">
<div
v-for="(event, index) in eventLog"
:key="index"
>{{ event }}</div>
</div>
</div>
</div>
<div class="mbe4">
<h2>External Label Support</h2>
<p class="mbs2 mbe3">
The Select component supports external labels with helper text, required fields, and validation states.
</p>
</div>
<div
class="mbe4"
style="max-width: 400px;"
>
<div class="mbe3">
<VueSelect
label="Favorite Framework"
name="framework"
>
<option value="">Choose a framework</option>
<option value="react">React</option>
<option value="vue">Vue</option>
<option value="angular">Angular</option>
<option value="svelte">Svelte</option>
</VueSelect>
</div>
<div class="mbe3">
<VueSelect
label="Country"
help-text="Select your country of residence"
name="country"
>
<option value="">Select a country</option>
<option value="us">United States</option>
<option value="uk">United Kingdom</option>
<option value="ca">Canada</option>
<option value="au">Australia</option>
</VueSelect>
</div>
<div class="mbe3">
<VueSelect
label="Preferred Language"
:required="true"
help-text="This field is required"
name="language"
>
<option value="">Select a language</option>
<option value="en">English</option>
<option value="es">Spanish</option>
<option value="fr">French</option>
<option value="de">German</option>
</VueSelect>
</div>
<div class="mbe3">
<VueSelect
label="Payment Method"
:required="true"
:invalid="true"
error-message="Please select a payment method to continue"
name="payment"
>
<option value="">Select payment method</option>
<option value="credit">Credit Card</option>
<option value="debit">Debit Card</option>
<option value="paypal">PayPal</option>
</VueSelect>
</div>
</div>
<div class="mbe4">
<h2>Label Positioning</h2>
<p class="mbs2 mbe3">
Control label position with <code>label-position</code>: 'top' (default), 'start', 'end', or 'bottom'.
<strong>Note:</strong> 'bottom' is not recommended as the dropdown will cover the label.
</p>
</div>
<div
class="mbe4"
style="max-width: 600px;"
>
<div class="mbe3">
<VueSelect
label="Top Label (Default)"
label-position="top"
name="pos-top"
>
<option value="">Select option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</VueSelect>
</div>
<div class="mbe3">
<VueSelect
label="Start Position"
label-position="start"
name="pos-start"
>
<option value="">Select option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</VueSelect>
</div>
<div class="mbe3">
<VueSelect
label="End Position"
label-position="end"
name="pos-end"
>
<option value="">Select option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</VueSelect>
</div>
<div class="mbe3">
<VueSelect
label="Bottom Position (Not Recommended)"
label-position="bottom"
name="pos-bottom"
help-text="The dropdown will cover this label when opened"
>
<option value="">Select option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</VueSelect>
</div>
</div>
<div class="mbe4">
<h2>CSS Shadow Parts Customization</h2>
<p class="mbs2 mbe3">
Use CSS Shadow Parts to customize the select's appearance.
</p>
</div>
<div
class="mbe4"
style="max-width: 400px;"
>
<VueSelect
label="Custom Styled Select"
name="custom"
class="custom-select"
>
<option value="">Select a color</option>
<option value="red">Red</option>
<option value="blue">Blue</option>
<option value="green">Green</option>
<option value="purple">Purple</option>
</VueSelect>
</div>
</section>
</template>
<script lang="ts">
import { defineComponent, ref } from "vue";
import { VueSelect } from "agnosticui-core/select/vue";
export default defineComponent({
name: "SelectExamples",
components: {
VueSelect,
},
setup() {
// Basic @change example
const selectedFruit = ref("");
// Vue wrapper emits detail payload directly, not full CustomEvent
const handleChange = (detail: { value: string | string[] }) => {
selectedFruit.value = detail.value as string;
};
// v-model examples
const selectedColor = ref("");
const selectedSports = ref<string[]>([]);
// Event logging example
const eventLog = ref<string[]>([]);
const logEvent = (message: string) => {
eventLog.value.unshift(message);
if (eventLog.value.length > 8) eventLog.value.pop();
};
const handleEventChange = (detail: { value: string | string[] }) => {
logEvent(`@change - value: ${detail.value}`);
};
const handleFocus = () => {
logEvent("@focus");
};
const handleBlur = () => {
logEvent("@blur");
};
const handleClick = () => {
logEvent("@click");
};
return {
selectedFruit,
handleChange,
selectedColor,
selectedSports,
eventLog,
handleEventChange,
handleFocus,
handleBlur,
handleClick,
};
},
});
</script>
<style scoped>
.custom-select::part(ag-select) {
border: 2px solid var(--ag-primary);
border-radius: var(--ag-radius-lg);
font-weight: 500;
background-color: var(--ag-background-secondary);
}
.custom-select::part(ag-select):focus {
border-color: var(--ag-primary-dark);
box-shadow: 0 0 0 3px var(--ag-blue-100);
}
</style>
Live Preview
View Lit / Web Component Code
import { LitElement, html, css } from 'lit';
import 'agnosticui-core/select';
export class SelectLitExamples extends LitElement {
static properties = {
selectedFruit: { type: String },
selectedColor: { type: String },
selectedSports: { type: Array },
eventLog: { type: Array }
};
constructor() {
super();
this.selectedFruit = '';
this.selectedColor = '';
this.selectedSports = [];
this.eventLog = [];
}
createRenderRoot() {
return this;
}
handleChange(e) {
this.selectedFruit = e.detail.value;
}
handleColorChange(e) {
this.selectedColor = e.detail.value;
}
handleSportsChange(e) {
this.selectedSports = Array.from(e.target.shadowRoot.querySelector('select').selectedOptions, option => option.value);
}
logEvent(message) {
this.eventLog = [message, ...this.eventLog].slice(0, 8);
this.requestUpdate();
}
handleEventChange(e) {
this.logEvent(`@change - value: ${e.detail.value}`);
}
render() {
return html`
<section>
<div class="mbe4">
<h2>Basic Select</h2>
<p class="mbs2 mbe3">
A standard select dropdown with custom styling.
</p>
</div>
<div class="mbe4" style="max-width: 400px;">
<ag-select
label="Greatest Tennis Player"
name="tennis"
>
<option value="">- Select a player -</option>
<option value="andre">Andre Agassi</option>
<option value="serena">Serena Williams</option>
<option value="roger">Roger Federer</option>
<option value="mac">John McEnroe</option>
<option value="martina">Martina Navratilova</option>
<option value="rafa">Rafael Nadal</option>
<option value="borg">Bjorn Borg</option>
<option value="althea">Althea Gibson</option>
</ag-select>
</div>
<div class="mbe4">
<h2>Size Variants</h2>
<p class="mbs2 mbe3">
The Select component comes in three sizes: small, default, and large.
</p>
</div>
<div
class="mbe4"
style="max-width: 400px;"
>
<div class="mbe3">
<ag-select
label="Small Select"
size="small"
name="small"
>
<option value="">Select an option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</ag-select>
</div>
<div class="mbe3">
<ag-select
label="Default Select"
name="default"
>
<option value="">Select an option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</ag-select>
</div>
<div class="mbe3">
<ag-select
label="Large Select"
size="large"
name="large"
>
<option value="">Select an option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</ag-select>
</div>
</div>
<div class="mbe4">
<h2>Disabled State</h2>
<p class="mbs2 mbe3">
Selects can be disabled to prevent user interaction.
</p>
</div>
<div
class="mbe4"
style="max-width: 400px;"
>
<ag-select
label="Disabled Select"
disabled
name="disabled"
>
<option value="">Cannot select</option>
<option value="1">Option 1</option>
</ag-select>
</div>
<div class="mbe4">
<h2>Multiple Selection</h2>
<p class="mbs2 mbe3">
Enable multiple selection mode with the <code>multiple</code> prop. Use Ctrl/Cmd + Click to select multiple options.
</p>
</div>
<div
class="mbe4"
style="max-width: 400px;"
>
<ag-select
label="Favorite Tennis Players (Select Multiple)"
multiple
multiple-size="6"
name="multiple"
@change="${this.handleSportsChange}"
>
<option value="andre">Andre Agassi</option>
<option value="serena">Serena Williams</option>
<option value="roger">Roger Federer</option>
<option value="mac">John McEnroe</option>
<option value="martina">Martina Navratilova</option>
<option value="rafa">Rafael Nadal</option>
<option value="borg">Bjorn Borg</option>
<option value="althea">Althea Gibson</option>
</ag-select>
${this.selectedSports.length > 0 ? html`
<p
style="margin-top: 0.75rem; padding: 0.75rem; background: var(--ag-background-secondary); border-radius: var(--ag-radius-md);"
>
You selected <strong>${this.selectedSports.length}</strong> sport(s): ${this.selectedSports.join(', ')}
</p>
` : ''}
</div>
<div class="mbe4">
<h2>With Change Handler</h2>
<p class="mbs2 mbe3">
Listen to selection changes with the <code>@change</code> event.
</p>
</div>
<div
class="mbe4"
style="max-width: 400px;"
>
<ag-select
label="Select a Fruit"
name="fruit"
@change="${this.handleChange}"
>
<option value="">Choose a fruit</option>
<option value="apple">Apple</option>
<option value="banana">Banana</option>
<option value="orange">Orange</option>
<option value="grape">Grape</option>
</ag-select>
${this.selectedFruit ? html`
<p
style="margin-top: 0.75rem; padding: 0.75rem; background: var(--ag-background-secondary); border-radius: var(--ag-radius-md);"
>
You selected: <strong>${this.selectedFruit}</strong>
</p>
` : ''}
</div>
<div class="mbe4">
<h2>Event Handling</h2>
<p class="mbs2 mbe3">
The Select component fires multiple events: <code>@change</code>, <code>@focus</code>, <code>@blur</code>, and <code>@click</code>.
</p>
</div>
<div
class="mbe4"
style="max-width: 600px;"
>
<ag-select
label="Interact with this select to see events"
name="events"
@change="${this.handleEventChange}"
@focus="${() => this.logEvent('@focus')}"
@blur="${() => this.logEvent('@blur')}"
@click="${() => this.logEvent('@click')}"
>
<option value="">Select an option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</ag-select>
${this.eventLog.length > 0 ? html`
<div
style="margin-top: 0.75rem; padding: 0.75rem; background: var(--ag-background-secondary); border-radius: var(--ag-radius-md);"
>
<strong>Event Log (most recent first):</strong>
<div style="margin-top: 0.5rem; font-family: monospace; font-size: 0.875rem; line-height: 1.6;">
${this.eventLog.map(event => html`<div>${event}</div>`)}
</div>
</div>
` : ''}
</div>
<div class="mbe4">
<h2>External Label Support</h2>
<p class="mbs2 mbe3">
The Select component supports external labels with helper text, required fields, and validation states.
</p>
</div>
<div
class="mbe4"
style="max-width: 400px;"
>
<div class="mbe3">
<ag-select
label="Favorite Framework"
name="framework"
>
<option value="">Choose a framework</option>
<option value="react">React</option>
<option value="vue">Vue</option>
<option value="angular">Angular</option>
<option value="svelte">Svelte</option>
</ag-select>
</div>
<div class="mbe3">
<ag-select
label="Country"
help-text="Select your country of residence"
name="country"
>
<option value="">Select a country</option>
<option value="us">United States</option>
<option value="uk">United Kingdom</option>
<option value="ca">Canada</option>
<option value="au">Australia</option>
</ag-select>
</div>
<div class="mbe3">
<ag-select
label="Preferred Language"
required
help-text="This field is required"
name="language"
>
<option value="">Select a language</option>
<option value="en">English</option>
<option value="es">Spanish</option>
<option value="fr">French</option>
<option value="de">German</option>
</ag-select>
</div>
<div class="mbe3">
<ag-select
label="Payment Method"
required
invalid
error-message="Please select a payment method to continue"
name="payment"
>
<option value="">Select payment method</option>
<option value="credit">Credit Card</option>
<option value="debit">Debit Card</option>
<option value="paypal">PayPal</option>
</ag-select>
</div>
</div>
<div class="mbe4">
<h2>Label Positioning</h2>
<p class="mbs2 mbe3">
Control label position with <code>label-position</code>: 'top' (default), 'start', 'end', or 'bottom'.
<strong>Note:</strong> 'bottom' is not recommended as the dropdown will cover the label.
</p>
</div>
<div
class="mbe4"
style="max-width: 600px;"
>
<div class="mbe3">
<ag-select
label="Top Label (Default)"
label-position="top"
name="pos-top"
>
<option value="">Select option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</ag-select>
</div>
<div class="mbe3">
<ag-select
label="Start Position"
label-position="start"
name="pos-start"
>
<option value="">Select option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</ag-select>
</div>
<div class="mbe3">
<ag-select
label="End Position"
label-position="end"
name="pos-end"
>
<option value="">Select option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</ag-select>
</div>
<div class="mbe3">
<ag-select
label="Bottom Position (Not Recommended)"
label-position="bottom"
name="pos-bottom"
help-text="The dropdown will cover this label when opened"
>
<option value="">Select option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</ag-select>
</div>
</div>
</section>
`;
}
}
customElements.define('select-lit-examples', SelectLitExamples);
Interactive Preview: Click the "Open in StackBlitz" button below to see this example running live in an interactive playground.
View React Code
import { ReactSelect } from "agnosticui-core/select/react";
import { useState } from "react";
export default function SelectReactExamples() {
// Basic example
const [selectedFruit, setSelectedFruit] = useState("");
// Multiple example
const [selectedSports, setSelectedSports] = useState([]);
// Event logging
const [eventLog, setEventLog] = useState([]);
const logEvent = (message) => {
setEventLog((prev) => [message, ...prev].slice(0, 8));
};
const handleEventChange = (e) => {
logEvent(`onChange - value: ${e.detail.value}`);
};
const handleMultipleChange = (e) => {
setSelectedSports(e.detail.value);
};
return (
<section>
<div className="mbe4">
<h2>Basic Select</h2>
<p className="mbs2 mbe3">
A standard select dropdown with custom styling.
</p>
</div>
<div className="mbe4" style={{ maxWidth: "400px" }}>
<ReactSelect
label="Greatest Tennis Player"
name="tennis"
>
<option value="">- Select a player -</option>
<option value="andre">Andre Agassi</option>
<option value="serena">Serena Williams</option>
<option value="roger">Roger Federer</option>
<option value="mac">John McEnroe</option>
<option value="martina">Martina Navratilova</option>
<option value="rafa">Rafael Nadal</option>
<option value="borg">Bjorn Borg</option>
<option value="althea">Althea Gibson</option>
</ReactSelect>
</div>
<div className="mbe4">
<h2>Size Variants</h2>
<p className="mbs2 mbe3">
The Select component comes in three sizes: small, default, and large.
</p>
</div>
<div className="mbe4" style={{ maxWidth: "400px" }}>
<div className="mbe3">
<ReactSelect
label="Small Select"
size="small"
name="small"
>
<option value="">Select an option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</ReactSelect>
</div>
<div className="mbe3">
<ReactSelect
label="Default Select"
name="default"
>
<option value="">Select an option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</ReactSelect>
</div>
<div className="mbe3">
<ReactSelect
label="Large Select"
size="large"
name="large"
>
<option value="">Select an option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</ReactSelect>
</div>
</div>
<div className="mbe4">
<h2>Disabled State</h2>
<p className="mbs2 mbe3">
Selects can be disabled to prevent user interaction.
</p>
</div>
<div className="mbe4" style={{ maxWidth: "400px" }}>
<ReactSelect
label="Disabled Select"
disabled
name="disabled"
>
<option value="">Cannot select</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</ReactSelect>
</div>
<div className="mbe4">
<h2>Multiple Selection</h2>
<p className="mbs2 mbe3">
Enable multiple selection mode with the <code>multiple</code> prop. Use Ctrl/Cmd + Click to select multiple options.
</p>
</div>
<div className="mbe4" style={{ maxWidth: "400px" }}>
<ReactSelect
label="Favorite Tennis Players (Select Multiple)"
multiple
multipleSize={6}
name="multiple"
onChange={handleMultipleChange}
>
<option value="andre">Andre Agassi</option>
<option value="serena">Serena Williams</option>
<option value="roger">Roger Federer</option>
<option value="mac">John McEnroe</option>
<option value="martina">Martina Navratilova</option>
<option value="rafa">Rafael Nadal</option>
<option value="borg">Bjorn Borg</option>
<option value="althea">Althea Gibson</option>
</ReactSelect>
{selectedSports.length > 0 && (
<p style={{ marginTop: "0.75rem", padding: "0.75rem", background: "var(--ag-background-secondary)", borderRadius: "var(--ag-radius-md)" }}>
You selected <strong>{selectedSports.length}</strong> sport(s): {selectedSports.join(', ')}
</p>
)}
</div>
<div className="mbe4">
<h2>With Change Handler</h2>
<p className="mbs2 mbe3">
Listen to selection changes with the <code>onChange</code> event.
</p>
</div>
<div className="mbe4" style={{ maxWidth: "400px" }}>
<ReactSelect
label="Select a Fruit"
name="fruit"
onChange={(e) => setSelectedFruit(e.detail.value)}
>
<option value="">Choose a fruit</option>
<option value="apple">Apple</option>
<option value="banana">Banana</option>
<option value="orange">Orange</option>
<option value="grape">Grape</option>
</ReactSelect>
{selectedFruit && (
<p style={{ marginTop: "0.75rem", padding: "0.75rem", background: "var(--ag-background-secondary)", borderRadius: "var(--ag-radius-md)" }}>
You selected: <strong>{selectedFruit}</strong>
</p>
)}
</div>
<div className="mbe4">
<h2>Event Handling</h2>
<p className="mbs2 mbe3">
The Select component fires multiple events: <code>onChange</code>, <code>onFocus</code>, <code>onBlur</code>, and <code>onClick</code>.
</p>
</div>
<div className="mbe4" style={{ maxWidth: "600px" }}>
<ReactSelect
label="Interact with this select to see events"
name="events"
onChange={handleEventChange}
onFocus={() => logEvent("onFocus")}
onBlur={() => logEvent("onBlur")}
onClick={() => logEvent("onClick")}
>
<option value="">Select an option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</ReactSelect>
{eventLog.length > 0 && (
<div style={{ marginTop: "0.75rem", padding: "0.75rem", background: "var(--ag-background-secondary)", borderRadius: "var(--ag-radius-md)" }}>
<strong>Event Log (most recent first):</strong>
<div style={{ marginTop: "0.5rem", fontFamily: "monospace", fontSize: "0.875rem", lineHeight: "1.6" }}>
{eventLog.map((event, index) => (
<div key={index}>{event}</div>
))}
</div>
</div>
)}
</div>
<div className="mbe4">
<h2>External Label Support</h2>
<p className="mbs2 mbe3">
The Select component supports external labels with helper text, required fields, and validation states.
</p>
</div>
<div className="mbe4" style={{ maxWidth: "400px" }}>
<div className="mbe3">
<ReactSelect
label="Favorite Framework"
name="framework"
>
<option value="">Choose a framework</option>
<option value="react">React</option>
<option value="vue">Vue</option>
<option value="angular">Angular</option>
<option value="svelte">Svelte</option>
</ReactSelect>
</div>
<div className="mbe3">
<ReactSelect
label="Country"
helpText="Select your country of residence"
name="country"
>
<option value="">Select a country</option>
<option value="us">United States</option>
<option value="uk">United Kingdom</option>
<option value="ca">Canada</option>
<option value="au">Australia</option>
</ReactSelect>
</div>
<div className="mbe3">
<ReactSelect
label="Preferred Language"
required
helpText="This field is required"
name="language"
>
<option value="">Select a language</option>
<option value="en">English</option>
<option value="es">Spanish</option>
<option value="fr">French</option>
<option value="de">German</option>
</ReactSelect>
</div>
<div className="mbe3">
<ReactSelect
label="Payment Method"
required
invalid
errorMessage="Please select a payment method to continue"
name="payment"
>
<option value="">Select payment method</option>
<option value="credit">Credit Card</option>
<option value="debit">Debit Card</option>
<option value="paypal">PayPal</option>
</ReactSelect>
</div>
</div>
<div className="mbe4">
<h2>Label Positioning</h2>
<p className="mbs2 mbe3">
Control label position with <code>labelPosition</code>: 'top' (default), 'start', 'end', or 'bottom'.
<strong>Note:</strong> 'bottom' is not recommended as the dropdown will cover the label.
</p>
</div>
<div className="mbe4" style={{ maxWidth: "600px" }}>
<div className="mbe3">
<ReactSelect
label="Top Label (Default)"
labelPosition="top"
name="pos-top"
>
<option value="">Select option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</ReactSelect>
</div>
<div className="mbe3">
<ReactSelect
label="Start Position"
labelPosition="start"
name="pos-start"
>
<option value="">Select option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</ReactSelect>
</div>
<div className="mbe3">
<ReactSelect
label="End Position"
labelPosition="end"
name="pos-end"
>
<option value="">Select option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</ReactSelect>
</div>
<div className="mbe3">
<ReactSelect
label="Bottom Position (Not Recommended)"
labelPosition="bottom"
name="pos-bottom"
helpText="The dropdown will cover this label when opened"
>
<option value="">Select option</option>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</ReactSelect>
</div>
</div>
<div className="mbe4">
<h2>CSS Shadow Parts Customization</h2>
<p className="mbs2 mbe3">
Use CSS Shadow Parts to customize the select's appearance.
</p>
</div>
<div className="mbe4" style={{ maxWidth: "400px" }}>
<style dangerouslySetInnerHTML={{__html: `
.custom-select::part(ag-select) {
border: 2px solid var(--ag-primary);
border-radius: var(--ag-radius-lg);
font-weight: 500;
background-color: var(--ag-background-secondary);
}
.custom-select::part(ag-select):focus {
border-color: var(--ag-primary-dark);
box-shadow: 0 0 0 3px var(--ag-blue-100);
}
`}} />
<ReactSelect
label="Custom Styled Select"
name="custom"
className="custom-select"
>
<option value="">Select a color</option>
<option value="red">Red</option>
<option value="blue">Blue</option>
<option value="green">Green</option>
<option value="purple">Purple</option>
</ReactSelect>
</div>
</section>
);
}
Usage
TIP
The framework examples below import AgnosticUI as an npm package. Alternatively, you can use the CLI for complete control, AI/LLM visibility, and full code ownership:
npx ag init --framework FRAMEWORK # react, vue, lit, svelte, etc.
npx ag add SelectThe CLI copies source code directly into your project, giving you full visibility and control. After running npx ag add, you'll receive exact import instructions.
Features
- Native select element - Uses browser's built-in
<select>with custom styling - Size variants - Small, default, and large sizes
- Multiple selection - Support for native multiple select with
sizeattribute - External label support - Built-in label, helper text, and error messages
- Validation states - Required and invalid states with accessible error messages
- Disabled state - Proper disabled styling and accessibility
- Custom dropdown arrow - SVG arrow icon for single select mode
- Accessibility - Maintains all native select accessibility features
- Responsive - 100% width by default, adapts to container
Installation
npm install agnosticui-coreyarn add agnosticui-corepnpm add agnosticui-corebun add agnosticui-coreComponent API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
size | 'small' | 'large' | '' | '' | Size variant of the select |
multiple | boolean | false | Enable multiple selection |
disabled | boolean | false | Disable the select |
name | string | '' | Form name attribute |
multipleSize | number | undefined | Number of visible options for multiple select |
label | string | '' | External label text |
labelPosition | 'top' | 'start' | 'end' | 'bottom' | 'top' | Position of the label relative to select. Note: 'bottom' is not recommended as the dropdown menu may cover the label |
labelHidden | boolean | false | Visually hides the label (still accessible to screen readers) |
noLabel | boolean | false | Removes the label element entirely |
required | boolean | false | Marks the select as required (adds asterisk to label) |
invalid | boolean | false | Marks the select as invalid (shows error state) |
errorMessage | string | '' | Error message displayed when invalid is true |
helpText | string | '' | Helper text displayed below the select |
Events
The Select component supports both custom and native events following the AgnosticUI v2 event conventions.
| Event | Framework | Detail | Description |
|---|---|---|---|
change | Vue: @changeReact: onChangeLit: @change | { value: string | string[] } | Fired when the selected value changes. Custom event with detail payload. |
focus | Vue: @focusReact: onFocusLit: @focus | FocusEvent | Fired when select receives focus. Native event, re-dispatched from host. |
blur | Vue: @blurReact: onBlurLit: @blur | FocusEvent | Fired when select loses focus. Native event, re-dispatched from host. |
click | Vue: @clickReact: onClickLit: @click | MouseEvent | Fired when select is clicked. Native event. |
Slots
| Slot | Description |
|---|---|
| default | Option elements |
CSS Shadow Parts
| Part | Description |
|---|---|
ag-select | The select element itself |
Accessibility
The Select component maintains all native select accessibility features:
- Semantic HTML - Uses native
<select>element - Label association - Supports proper
id/forlabel relationships (both external labels and manual labels) - ARIA relationships - Automatically links helper text and error messages via
aria-describedby - Required fields - Visual asterisk indicator and proper
requiredattribute - Error states -
aria-invalidattribute and associated error messages - Keyboard navigation - Full keyboard support (arrow keys, Enter, Space, Escape)
- Screen reader support - Native screen reader announcements with enhanced context from helper/error text
- Focus management - Clear focus indicator with high contrast mode support
- Disabled state - Proper
disabledandaria-disabledattributes
Customization
You can customize the Select component using CSS Shadow Parts:
ag-select::part(ag-select) {
border: 2px solid blue;
border-radius: 8px;
font-size: 1.2rem;
}
ag-select::part(ag-select):focus {
border-color: darkblue;
box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.3);
}Design Tokens
The Select component uses the following design tokens:
--ag-space-*- Padding and spacing--ag-font-size-*- Font sizes for different sizes--ag-text-primary- Text color--ag-border- Border color--ag-radius-md- Border radius--ag-focus-ring-color- Focus ring color--ag-disabled-bg- Disabled background--ag-transition-fast- Transition timing
Best Practices
- Always provide a label - Use visible labels for better accessibility
- Include a default option - Provide a placeholder/prompt option with empty value
- Use meaningful option text - Clear, descriptive option labels
- Consider multiple select - For selecting multiple items, set
multipleandmultipleSize - Handle change events - Process selection changes appropriately
- Disable when needed - Use
disabledfor unavailable states - Size appropriately - Use
sizeprop for visual hierarchy