Select
SelectTrigger
Build custom select triggers with full control over input and display
SelectTrigger
SelectTrigger is a render prop component that gives you complete control over the select button/trigger appearance. Build custom search inputs, chip displays, and complex multi-select interfaces.
Basic Custom Trigger
Replace the default select button with a custom input and chip display:
'use client';
import { useState } from 'react';
import { Select, SelectOption, SelectTrigger, Input } from '@geckoui/geckoui';
function Example() {
const [values, setValues] = useState<number[]>([]);
return (
<Select multiple value={values} onChange={setValues}>
<SelectTrigger<number> multiple>
{({
keyword,
selectedOptions,
handleInputChange,
handleKeyboardInteraction,
openMenu
}) => (
<div>
<Input
placeholder="Search months..."
value={keyword}
onChange={handleInputChange}
onKeyDown={handleKeyboardInteraction}
onFocus={openMenu}
autoComplete="off"
autoCapitalize="off"
autoCorrect="off"
spellCheck={false}
/>
{selectedOptions.length > 0 && (
<div className="mt-2 flex flex-wrap gap-2">
{selectedOptions.map((option) => (
<span
key={option.value}
className="bg-blue-100 text-blue-800 px-2 py-1 rounded text-sm"
>
{option.label}
</span>
))}
</div>
)}
</div>
)}
</SelectTrigger>
{Array.from({ length: 12 }, (_, i) => (
<SelectOption
key={i}
value={i}
label={new Date(0, i).toLocaleString('default', { month: 'long' })}
/>
))}
</Select>
);
}Chips with Remove Buttons
Add remove buttons to each chip:
ReactVue
import { X } from 'lucide-react';
<Select multiple value={values} onChange={setValues}>
<SelectTrigger<number> multiple>
{({
keyword,
selectedOptions,
handleChange,
handleInputChange,
handleKeyboardInteraction,
openMenu
}) => (
<div>
<Input
placeholder="Type to search..."
value={keyword}
onChange={handleInputChange}
onKeyDown={handleKeyboardInteraction}
onFocus={openMenu}
autoComplete="off"
/>
{selectedOptions.length > 0 && (
<div className="mt-2 flex flex-wrap gap-2">
{selectedOptions.map((option) => (
<span
key={option.value}
className="inline-flex items-center gap-1 bg-purple-100 text-purple-800 px-2 py-1 rounded"
>
{option.label}
<button
type="button"
onClick={(e) => {
e.stopPropagation();
handleChange(option.value);
}}
className="hover:bg-purple-200 rounded-full p-0.5"
>
<X className="w-3 h-3" />
</button>
</span>
))}
</div>
)}
</div>
)}
</SelectTrigger>
<SelectOption value="react" label="React" />
<SelectOption value="vue" label="Vue" />
<SelectOption value="angular" label="Angular" />
</Select>Single Select with Custom Trigger
Use SelectTrigger for single selection mode:
const [value, setValue] = useState<string | null>(null);
<Select value={value} onChange={setValue}>
<SelectTrigger>
{({
keyword,
selectedOptions,
handleInputChange,
handleKeyboardInteraction,
openMenu
}) => (
<div className="space-y-2">
<Input
placeholder="Search frameworks..."
value={keyword}
onChange={handleInputChange}
onKeyDown={handleKeyboardInteraction}
onFocus={openMenu}
autoComplete="off"
/>
{selectedOptions && (
<div className="p-2 bg-green-50 border border-green-200 rounded">
<span className="text-sm text-green-800">
Selected: <strong>{selectedOptions.label}</strong>
</span>
</div>
)}
</div>
)}
</SelectTrigger>
<SelectOption value="next" label="Next.js" />
<SelectOption value="remix" label="Remix" />
</Select>Note: For single select, selectedOptions is a single object, not an array.
Custom Styled Chips
Use Button components for chips:
import { Button } from '@geckoui/geckoui';
<Select multiple value={values} onChange={setValues}>
<SelectTrigger<number> multiple>
{({ selectedOptions, handleChange, /* ... */ }) => (
<div className="space-y-3">
<Input placeholder="Select programming languages..." />
{selectedOptions.length > 0 && (
<div>
<div className="text-xs text-gray-500 mb-2">
{selectedOptions.length} selected
</div>
<div className="flex flex-wrap gap-2">
{selectedOptions.map((option) => (
<Button
key={option.value}
type="button"
variant="outlined"
size="sm"
onClick={(e) => {
e.stopPropagation();
handleChange(option.value);
}}
className="gap-2"
>
<span>{option.label}</span>
<X className="w-3 h-3" />
</Button>
))}
</div>
</div>
)}
</div>
)}
</SelectTrigger>
{/* ... options */}
</Select>Props API
| Prop | Type | Description |
|---|---|---|
multiple | boolean | Must match the Select's multiple prop |
children | (args) => ReactNode | Render function receiving trigger state |
Render Function Arguments
For Multiple Select (multiple={true})
{
keyword: string;
selectedOptions: Array<{ label: string; value: T }>;
handleChange: (value: T) => void;
options: Array<{ label: string; value: T; /* ... */ }>;
toggleMenu: () => void;
open: boolean;
openMenu: () => void;
closeMenu: () => void;
hasValue: boolean;
filteredOptions: Array<{ label: string; value: T; /* ... */ }>;
handleInputChange: (e: ChangeEvent<HTMLInputElement>) => void;
handleKeyboardInteraction: (e: KeyboardEvent<HTMLInputElement>) => void;
}For Single Select (multiple={false} or omitted)
{
keyword: string;
selectedOptions: { label: string; value: T } | null;
handleChange: (value: T) => void;
// ... same as multiple
}Key difference: selectedOptions is an array for multiple select, but a single object (or null) for single select.
Keyboard Navigation
The handleKeyboardInteraction function handles:
- Arrow Up/Down: Navigate options
- Enter: Select focused option.
- Tab: Navigate options.
- Escape: Close menu (handled by Select)
Always attach it to your input:
<Input
onKeyDown={handleKeyboardInteraction}
// ... other props
/>Best Practices
- Always include
autoComplete="off"on custom inputs to prevent browser suggestions - Use
e.stopPropagation()on chip remove buttons to prevent menu toggling - Match
multipleprop on SelectTrigger with Select'smultipleprop - Handle both keyboard and mouse interactions for accessibility
- Show visual feedback for selected items and focus states
Related Pages
- Multiple Selection - Multi-select patterns
- Filterable - Built-in search without custom trigger
- SelectOption - Customize option display
- Advanced - Programmatic control with useSelect hook