import React, { useState } from 'react';
import Select, { MultiValue, SingleValue, StylesConfig } from 'react-select';
import CreatableSelect from 'react-select/creatable';
import AsyncSelect from 'react-select/async';
interface Option {
value: string;
label: string;
color?: string;
}
const fruitOptions: Option[] = [
{ value: 'apple', label: '๐ Apple', color: '#ff6b6b' },
{ value: 'banana', label: '๐ Banana', color: '#ffd93d' },
{ value: 'orange', label: '๐ Orange', color: '#ff9f43' },
{ value: 'grape', label: '๐ Grape', color: '#a55eea' },
{ value: 'watermelon', label: '๐ Watermelon', color: '#26de81' },
{ value: 'strawberry', label: '๐ Strawberry', color: '#fc5c65' },
];
const colorOptions: Option[] = [
{ value: 'red', label: 'Red', color: '#ef4444' },
{ value: 'blue', label: 'Blue', color: '#3b82f6' },
{ value: 'green', label: 'Green', color: '#22c55e' },
{ value: 'purple', label: 'Purple', color: '#a855f7' },
{ value: 'orange', label: 'Orange', color: '#f97316' },
];
const customStyles: StylesConfig<Option, false> = {
control: (base, state) => ({
...base,
borderColor: state.isFocused ? '#6366f1' : '#e2e8f0',
boxShadow: state.isFocused ? '0 0 0 2px rgba(99, 102, 241, 0.2)' : 'none',
'&:hover': { borderColor: '#6366f1' },
}),
option: (base, { data, isFocused, isSelected }) => ({
...base,
backgroundColor: isSelected
? data.color || '#6366f1'
: isFocused
? `${data.color}20` || '#f1f5f9'
: 'white',
color: isSelected ? 'white' : '#1e293b',
cursor: 'pointer',
}),
multiValue: (base, { data }) => ({
...base,
backgroundColor: `${data.color}20` || '#e2e8f0',
}),
multiValueLabel: (base, { data }) => ({
...base,
color: data.color || '#1e293b',
fontWeight: 500,
}),
};
export default function App() {
const [basicSelect, setBasicSelect] = useState<Option | null>(null);
const [multiSelect, setMultiSelect] = useState<Option[]>([]);
const [creatableValue, setCreatableValue] = useState<Option | null>(null);
const filterOptions = (inputValue: string): Option[] => {
return fruitOptions.filter((i) =>
i.label.toLowerCase().includes(inputValue.toLowerCase())
);
};
const loadOptions = (
inputValue: string,
callback: (options: Option[]) => void
) => {
setTimeout(() => {
callback(filterOptions(inputValue));
}, 500);
};
return (
<div style={styles.container}>
<div style={styles.header}>
<h1 style={styles.title}>React Select</h1>
<p style={styles.subtitle}>
Flexible and beautiful Select Input for React
</p>
</div>
<div style={styles.grid}>
{}
<div style={styles.card}>
<h3 style={styles.cardTitle}>Basic Select</h3>
<p style={styles.cardDesc}>Simple single selection</p>
<Select<Option>
value={basicSelect}
onChange={(option) => setBasicSelect(option)}
options={fruitOptions}
placeholder="Choose a fruit..."
isClearable
styles={customStyles}
/>
{basicSelect && (
<div style={styles.result}>
Selected: <strong>{basicSelect.label}</strong>
</div>
)}
</div>
{}
<div style={styles.card}>
<h3 style={styles.cardTitle}>Multi Select</h3>
<p style={styles.cardDesc}>Select multiple options</p>
<Select<Option, true>
isMulti
value={multiSelect}
onChange={(options) => setMultiSelect(options as Option[])}
options={colorOptions}
placeholder="Pick colors..."
styles={customStyles as StylesConfig<Option, true>}
closeMenuOnSelect={false}
/>
{multiSelect.length > 0 && (
<div style={styles.result}>
Selected: {multiSelect.map((o) => o.label).join(', ')}
</div>
)}
</div>
{}
<div style={styles.card}>
<h3 style={styles.cardTitle}>Creatable Select</h3>
<p style={styles.cardDesc}>Create new options on the fly</p>
<CreatableSelect<Option>
value={creatableValue}
onChange={(option) => setCreatableValue(option)}
options={fruitOptions}
placeholder="Type to create..."
isClearable
styles={customStyles}
formatCreateLabel={(input) => `Create "${input}"`}
/>
{creatableValue && (
<div style={styles.result}>
Value: <strong>{creatableValue.label}</strong>
{creatableValue.__isNew__ && (
<span style={styles.newBadge}>NEW</span>
)}
</div>
)}
</div>
{}
<div style={styles.card}>
<h3 style={styles.cardTitle}>Async Select</h3>
<p style={styles.cardDesc}>Load options asynchronously</p>
<AsyncSelect<Option>
cacheOptions
defaultOptions={fruitOptions}
loadOptions={loadOptions}
placeholder="Search fruits..."
styles={customStyles}
loadingMessage={() => 'Loading...'}
noOptionsMessage={() => 'No fruits found'}
/>
</div>
{}
<div style={styles.card}>
<h3 style={styles.cardTitle}>States Demo</h3>
<p style={styles.cardDesc}>Disabled and loading states</p>
<div style={{ marginBottom: 12 }}>
<Select<Option>
options={fruitOptions}
placeholder="Disabled select"
isDisabled
styles={customStyles}
/>
</div>
<Select<Option>
options={fruitOptions}
placeholder="Loading state"
isLoading
styles={customStyles}
/>
</div>
{}
<div style={styles.card}>
<h3 style={styles.cardTitle}>Grouped Options</h3>
<p style={styles.cardDesc}>Options organized in groups</p>
<Select<Option>
options={[
{
label: 'Fruits',
options: fruitOptions.slice(0, 3),
},
{
label: 'Colors',
options: colorOptions.slice(0, 3),
},
]}
placeholder="Select from groups..."
styles={customStyles}
/>
</div>
</div>
<div style={styles.footer}>
<p>โจ Fully customizable with TypeScript support</p>
</div>
</div>
);
}
const styles: { [key: string]: React.CSSProperties } = {
container: {
minHeight: '100vh',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
padding: 24,
fontFamily: 'system-ui, -apple-system, sans-serif',
},
header: {
textAlign: 'center',
marginBottom: 32,
color: 'white',
},
title: {
fontSize: 36,
fontWeight: 700,
margin: 0,
textShadow: '0 2px 4px rgba(0,0,0,0.2)',
},
subtitle: {
fontSize: 16,
opacity: 0.9,
marginTop: 8,
},
grid: {
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
gap: 20,
maxWidth: 1000,
margin: '0 auto',
},
card: {
background: 'white',
borderRadius: 12,
padding: 20,
boxShadow: '0 4px 20px rgba(0,0,0,0.15)',
},
cardTitle: {
margin: '0 0 4px 0',
fontSize: 18,
color: '#1e293b',
},
cardDesc: {
margin: '0 0 16px 0',
fontSize: 13,
color: '#64748b',
},
result: {
marginTop: 12,
padding: '8px 12px',
background: '#f1f5f9',
borderRadius: 6,
fontSize: 14,
color: '#475569',
},
newBadge: {
marginLeft: 8,
padding: '2px 6px',
background: '#22c55e',
color: 'white',
borderRadius: 4,
fontSize: 10,
fontWeight: 600,
},
footer: {
textAlign