0
# Customization
1
2
React-Select provides extensive customization capabilities through component replacement, styling systems, theming, and animated components. Every visual and behavioral aspect can be customized while maintaining accessibility and type safety.
3
4
## Capabilities
5
6
### Component System
7
8
Complete component replacement system allowing customization of any UI element.
9
10
```typescript { .api }
11
/**
12
* Configuration object for replacing default components
13
* All components are optional and will fall back to defaults if not provided
14
*/
15
interface SelectComponentsConfig<Option, IsMulti extends boolean, Group extends GroupBase<Option>> {
16
/** Clear indicator button component */
17
ClearIndicator?: ComponentType<ClearIndicatorProps<Option, IsMulti, Group>>;
18
/** Main control container component */
19
Control?: ComponentType<ControlProps<Option, IsMulti, Group>>;
20
/** Dropdown indicator arrow component */
21
DropdownIndicator?: ComponentType<DropdownIndicatorProps<Option, IsMulti, Group>>;
22
/** Down chevron icon component */
23
DownChevron?: ComponentType<any>;
24
/** Cross/close icon component */
25
CrossIcon?: ComponentType<any>;
26
/** Option group container component */
27
Group?: ComponentType<GroupProps<Option, IsMulti, Group>>;
28
/** Group heading component */
29
GroupHeading?: ComponentType<GroupHeadingProps<Option, IsMulti, Group>>;
30
/** Container for all indicators */
31
IndicatorsContainer?: ComponentType<IndicatorsContainerProps<Option, IsMulti, Group>>;
32
/** Separator between indicators */
33
IndicatorSeparator?: ComponentType<IndicatorSeparatorProps<Option, IsMulti, Group>>;
34
/** Search input component */
35
Input?: ComponentType<InputProps<Option, IsMulti, Group>>;
36
/** Loading spinner indicator */
37
LoadingIndicator?: ComponentType<LoadingIndicatorProps<Option, IsMulti, Group>>;
38
/** Dropdown menu container */
39
Menu?: ComponentType<MenuProps<Option, IsMulti, Group>>;
40
/** Scrollable menu list */
41
MenuList?: ComponentType<MenuListProps<Option, IsMulti, Group>>;
42
/** Portal for menu rendering */
43
MenuPortal?: ComponentType<MenuPortalProps<Option, IsMulti, Group>>;
44
/** Loading state message */
45
LoadingMessage?: ComponentType<NoticeProps<Option, IsMulti, Group>>;
46
/** No options available message */
47
NoOptionsMessage?: ComponentType<NoticeProps<Option, IsMulti, Group>>;
48
/** Multi-value item container */
49
MultiValue?: ComponentType<MultiValueProps<Option, IsMulti, Group>>;
50
/** Multi-value wrapper container */
51
MultiValueContainer?: ComponentType<MultiValueGenericProps<Option, IsMulti, Group>>;
52
/** Multi-value label text */
53
MultiValueLabel?: ComponentType<MultiValueGenericProps<Option, IsMulti, Group>>;
54
/** Multi-value remove button */
55
MultiValueRemove?: ComponentType<MultiValueRemoveProps<Option, IsMulti, Group>>;
56
/** Individual option component */
57
Option?: ComponentType<OptionProps<Option, IsMulti, Group>>;
58
/** Placeholder text component */
59
Placeholder?: ComponentType<PlaceholderProps<Option, IsMulti, Group>>;
60
/** Outermost select container */
61
SelectContainer?: ComponentType<ContainerProps<Option, IsMulti, Group>>;
62
/** Single selected value display */
63
SingleValue?: ComponentType<SingleValueProps<Option, IsMulti, Group>>;
64
/** Container for values and input */
65
ValueContainer?: ComponentType<ValueContainerProps<Option, IsMulti, Group>>;
66
}
67
68
/**
69
* Default components object containing all built-in components
70
*/
71
const components: SelectComponentsConfig<any, boolean, GroupBase<any>>;
72
```
73
74
**Usage Examples:**
75
76
```typescript
77
import Select, { components } from "react-select";
78
79
// Custom Option component with icons
80
const CustomOption = (props) => (
81
<components.Option {...props}>
82
<div style={{ display: 'flex', alignItems: 'center' }}>
83
<img src={props.data.icon} alt="" style={{ width: 20, height: 20, marginRight: 8 }} />
84
{props.children}
85
</div>
86
</components.Option>
87
);
88
89
// Custom Control with additional buttons
90
const CustomControl = (props) => (
91
<div style={{ position: 'relative' }}>
92
<components.Control {...props} />
93
<button
94
style={{ position: 'absolute', right: 30, top: '50%', transform: 'translateY(-50%)' }}
95
onClick={() => console.log('Custom button clicked')}
96
>
97
⭐
98
</button>
99
</div>
100
);
101
102
// Usage
103
const CustomizedSelect = () => (
104
<Select
105
options={optionsWithIcons}
106
components={{
107
Option: CustomOption,
108
Control: CustomControl,
109
}}
110
/>
111
);
112
113
// Custom MultiValue component
114
const CustomMultiValue = (props) => (
115
<components.MultiValue {...props} className="custom-multi-value">
116
<span className="tag-icon">🏷️</span>
117
<components.MultiValueLabel {...props} />
118
<components.MultiValueRemove {...props} />
119
</components.MultiValue>
120
);
121
122
// Custom loading and no-options messages
123
const CustomMessages = () => (
124
<Select
125
components={{
126
LoadingMessage: () => <div>🔄 Searching...</div>,
127
NoOptionsMessage: ({ inputValue }) => (
128
<div>😔 No results for "{inputValue}"</div>
129
),
130
}}
131
/>
132
);
133
```
134
135
### Styling System
136
137
Comprehensive styling system with CSS-in-JS, custom styles, and theme support.
138
139
```typescript { .api }
140
/**
141
* Style configuration object for customizing component appearance
142
* Each key corresponds to a component and receives props for dynamic styling
143
*/
144
interface StylesConfig<Option, IsMulti extends boolean, Group extends GroupBase<Option>> {
145
/** Outer container styles */
146
container?: (base: CSSObject, state: StylesProps) => CSSObject;
147
/** Main control styles */
148
control?: (base: CSSObject, state: ControlProps<Option, IsMulti, Group>) => CSSObject;
149
/** Dropdown indicator styles */
150
dropdownIndicator?: (base: CSSObject, state: DropdownIndicatorProps<Option, IsMulti, Group>) => CSSObject;
151
/** Group container styles */
152
group?: (base: CSSObject, state: GroupProps<Option, IsMulti, Group>) => CSSObject;
153
/** Group heading styles */
154
groupHeading?: (base: CSSObject, state: GroupHeadingProps<Option, IsMulti, Group>) => CSSObject;
155
/** Indicators container styles */
156
indicatorsContainer?: (base: CSSObject, state: IndicatorsContainerProps<Option, IsMulti, Group>) => CSSObject;
157
/** Indicator separator styles */
158
indicatorSeparator?: (base: CSSObject, state: IndicatorSeparatorProps<Option, IsMulti, Group>) => CSSObject;
159
/** Input element styles */
160
input?: (base: CSSObject, state: InputProps<Option, IsMulti, Group>) => CSSObject;
161
/** Loading indicator styles */
162
loadingIndicator?: (base: CSSObject, state: LoadingIndicatorProps<Option, IsMulti, Group>) => CSSObject;
163
/** Loading message styles */
164
loadingMessage?: (base: CSSObject, state: NoticeProps<Option, IsMulti, Group>) => CSSObject;
165
/** Menu container styles */
166
menu?: (base: CSSObject, state: MenuProps<Option, IsMulti, Group>) => CSSObject;
167
/** Menu list styles */
168
menuList?: (base: CSSObject, state: MenuListProps<Option, IsMulti, Group>) => CSSObject;
169
/** Menu portal styles */
170
menuPortal?: (base: CSSObject, state: MenuPortalProps<Option, IsMulti, Group>) => CSSObject;
171
/** Multi-value item styles */
172
multiValue?: (base: CSSObject, state: MultiValueProps<Option, IsMulti, Group>) => CSSObject;
173
/** Multi-value label styles */
174
multiValueLabel?: (base: CSSObject, state: MultiValueGenericProps<Option, IsMulti, Group>) => CSSObject;
175
/** Multi-value remove button styles */
176
multiValueRemove?: (base: CSSObject, state: MultiValueRemoveProps<Option, IsMulti, Group>) => CSSObject;
177
/** No options message styles */
178
noOptionsMessage?: (base: CSSObject, state: NoticeProps<Option, IsMulti, Group>) => CSSObject;
179
/** Individual option styles */
180
option?: (base: CSSObject, state: OptionProps<Option, IsMulti, Group>) => CSSObject;
181
/** Placeholder text styles */
182
placeholder?: (base: CSSObject, state: PlaceholderProps<Option, IsMulti, Group>) => CSSObject;
183
/** Single value display styles */
184
singleValue?: (base: CSSObject, state: SingleValueProps<Option, IsMulti, Group>) => CSSObject;
185
/** Value container styles */
186
valueContainer?: (base: CSSObject, state: ValueContainerProps<Option, IsMulti, Group>) => CSSObject;
187
}
188
189
/**
190
* Utility function for merging style configurations
191
* @param source - Base styles configuration
192
* @param target - Override styles configuration
193
* @returns Merged styles configuration
194
*/
195
function mergeStyles<Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
196
source: StylesConfig<Option, IsMulti, Group>,
197
target: StylesConfig<Option, IsMulti, Group>
198
): StylesConfig<Option, IsMulti, Group>;
199
```
200
201
**Usage Examples:**
202
203
```typescript
204
// Basic style customization
205
const customStyles = {
206
control: (base, state) => ({
207
...base,
208
border: state.isFocused ? '2px solid blue' : '1px solid gray',
209
boxShadow: state.isFocused ? '0 0 0 1px blue' : 'none',
210
'&:hover': {
211
border: '1px solid blue',
212
},
213
}),
214
option: (base, state) => ({
215
...base,
216
backgroundColor: state.isSelected
217
? 'blue'
218
: state.isFocused
219
? 'lightblue'
220
: 'white',
221
color: state.isSelected ? 'white' : 'black',
222
}),
223
multiValue: (base) => ({
224
...base,
225
backgroundColor: '#e3f2fd',
226
border: '1px solid #2196f3',
227
}),
228
multiValueLabel: (base) => ({
229
...base,
230
color: '#1976d2',
231
fontWeight: 'bold',
232
}),
233
};
234
235
const StyledSelect = () => (
236
<Select
237
options={options}
238
styles={customStyles}
239
isMulti
240
/>
241
);
242
243
// Dynamic styles based on data
244
const conditionalStyles = {
245
option: (base, { data, isSelected, isFocused }) => ({
246
...base,
247
backgroundColor: data.isUrgent
248
? (isSelected ? '#d32f2f' : isFocused ? '#ffcdd2' : '#fff')
249
: (isSelected ? '#1976d2' : isFocused ? '#e3f2fd' : '#fff'),
250
borderLeft: data.isUrgent ? '4px solid #d32f2f' : 'none',
251
}),
252
singleValue: (base, { data }) => ({
253
...base,
254
color: data.isUrgent ? '#d32f2f' : '#333',
255
fontWeight: data.isUrgent ? 'bold' : 'normal',
256
}),
257
};
258
259
// Responsive styles
260
const responsiveStyles = {
261
control: (base, state) => ({
262
...base,
263
minWidth: '200px',
264
'@media (max-width: 768px)': {
265
minWidth: '150px',
266
fontSize: '14px',
267
},
268
}),
269
menu: (base) => ({
270
...base,
271
zIndex: 9999,
272
'@media (max-width: 768px)': {
273
position: 'fixed',
274
top: '50%',
275
left: '50%',
276
transform: 'translate(-50%, -50%)',
277
width: '90vw',
278
maxWidth: '400px',
279
},
280
}),
281
};
282
```
283
284
### Theme System
285
286
Comprehensive theming system with colors, spacing, and responsive breakpoints.
287
288
```typescript { .api }
289
/**
290
* Complete theme configuration with colors, spacing, and other design tokens
291
*/
292
interface Theme {
293
/** Border radius values */
294
borderRadius: number;
295
/** Complete color palette */
296
colors: Colors;
297
/** Spacing configuration */
298
spacing: ThemeSpacing;
299
}
300
301
/**
302
* Color palette with semantic color names and variants
303
*/
304
interface Colors {
305
/** Primary brand colors */
306
primary: string;
307
primary75: string;
308
primary50: string;
309
primary25: string;
310
311
/** Danger/error colors */
312
danger: string;
313
dangerLight: string;
314
315
/** Neutral grayscale colors */
316
neutral0: string; // White
317
neutral5: string;
318
neutral10: string;
319
neutral20: string;
320
neutral30: string;
321
neutral40: string;
322
neutral50: string;
323
neutral60: string;
324
neutral70: string;
325
neutral80: string;
326
neutral90: string; // Near black
327
}
328
329
/**
330
* Spacing and sizing configuration
331
*/
332
interface ThemeSpacing {
333
/** Base unit for consistent spacing */
334
baseUnit: number;
335
/** Standard control height */
336
controlHeight: number;
337
/** Menu gutter spacing */
338
menuGutter: number;
339
}
340
341
/**
342
* Theme configuration - can be object or function
343
*/
344
type ThemeConfig = Theme | ((theme: Theme) => Theme);
345
346
/**
347
* Default theme object
348
*/
349
const defaultTheme: Theme;
350
```
351
352
**Usage Examples:**
353
354
```typescript
355
import Select, { defaultTheme } from "react-select";
356
357
// Custom theme object
358
const customTheme = {
359
...defaultTheme,
360
colors: {
361
...defaultTheme.colors,
362
primary: '#6366f1', // Indigo
363
primary75: '#8b5cf6',
364
primary50: '#a78bfa',
365
primary25: '#c4b5fd',
366
danger: '#ef4444', // Red
367
dangerLight: '#fecaca',
368
},
369
spacing: {
370
...defaultTheme.spacing,
371
baseUnit: 6, // Larger base unit
372
controlHeight: 44, // Taller controls
373
},
374
borderRadius: 8, // More rounded
375
};
376
377
const ThemedSelect = () => (
378
<Select
379
options={options}
380
theme={customTheme}
381
/>
382
);
383
384
// Theme function for dynamic theming
385
const createTheme = (isDark: boolean) => (theme: Theme) => ({
386
...theme,
387
colors: {
388
...theme.colors,
389
neutral0: isDark ? '#1f2937' : '#ffffff',
390
neutral10: isDark ? '#374151' : '#f3f4f6',
391
neutral20: isDark ? '#4b5563' : '#e5e7eb',
392
neutral90: isDark ? '#f9fafb' : '#111827',
393
primary: isDark ? '#60a5fa' : '#3b82f6',
394
},
395
});
396
397
const DynamicThemedSelect = ({ isDark }) => (
398
<Select
399
options={options}
400
theme={createTheme(isDark)}
401
/>
402
);
403
404
// Brand-specific theme
405
const brandTheme = (theme) => ({
406
...theme,
407
colors: {
408
...theme.colors,
409
primary: '#ff6b35', // Brand orange
410
primary75: '#ff8c42',
411
primary50: '#ffad42',
412
primary25: '#ffcd42',
413
},
414
spacing: {
415
...theme.spacing,
416
baseUnit: 4,
417
controlHeight: 40,
418
},
419
});
420
```
421
422
### Class Names System
423
424
Dynamic CSS class name system for external stylesheet integration.
425
426
```typescript { .api }
427
/**
428
* Configuration for dynamic class names based on component state
429
* Each function receives component props and returns class name string
430
*/
431
interface ClassNamesConfig<Option, IsMulti extends boolean, Group extends GroupBase<Option>> {
432
/** Container class names */
433
container?: (state: StylesProps) => string;
434
/** Control class names */
435
control?: (state: ControlProps<Option, IsMulti, Group>) => string;
436
/** Dropdown indicator class names */
437
dropdownIndicator?: (state: DropdownIndicatorProps<Option, IsMulti, Group>) => string;
438
/** Group class names */
439
group?: (state: GroupProps<Option, IsMulti, Group>) => string;
440
/** Group heading class names */
441
groupHeading?: (state: GroupHeadingProps<Option, IsMulti, Group>) => string;
442
/** Indicators container class names */
443
indicatorsContainer?: (state: IndicatorsContainerProps<Option, IsMulti, Group>) => string;
444
/** Indicator separator class names */
445
indicatorSeparator?: (state: IndicatorSeparatorProps<Option, IsMulti, Group>) => string;
446
/** Input class names */
447
input?: (state: InputProps<Option, IsMulti, Group>) => string;
448
/** Loading indicator class names */
449
loadingIndicator?: (state: LoadingIndicatorProps<Option, IsMulti, Group>) => string;
450
/** Loading message class names */
451
loadingMessage?: (state: NoticeProps<Option, IsMulti, Group>) => string;
452
/** Menu class names */
453
menu?: (state: MenuProps<Option, IsMulti, Group>) => string;
454
/** Menu list class names */
455
menuList?: (state: MenuListProps<Option, IsMulti, Group>) => string;
456
/** Menu portal class names */
457
menuPortal?: (state: MenuPortalProps<Option, IsMulti, Group>) => string;
458
/** Multi-value class names */
459
multiValue?: (state: MultiValueProps<Option, IsMulti, Group>) => string;
460
/** Multi-value label class names */
461
multiValueLabel?: (state: MultiValueGenericProps<Option, IsMulti, Group>) => string;
462
/** Multi-value remove class names */
463
multiValueRemove?: (state: MultiValueRemoveProps<Option, IsMulti, Group>) => string;
464
/** No options message class names */
465
noOptionsMessage?: (state: NoticeProps<Option, IsMulti, Group>) => string;
466
/** Option class names */
467
option?: (state: OptionProps<Option, IsMulti, Group>) => string;
468
/** Placeholder class names */
469
placeholder?: (state: PlaceholderProps<Option, IsMulti, Group>) => string;
470
/** Single value class names */
471
singleValue?: (state: SingleValueProps<Option, IsMulti, Group>) => string;
472
/** Value container class names */
473
valueContainer?: (state: ValueContainerProps<Option, IsMulti, Group>) => string;
474
}
475
```
476
477
**Usage Examples:**
478
479
```typescript
480
// Tailwind CSS integration
481
const tailwindClassNames = {
482
control: (state) =>
483
`border rounded-lg px-3 py-2 ${
484
state.isFocused
485
? 'border-blue-500 ring-2 ring-blue-200'
486
: 'border-gray-300 hover:border-gray-400'
487
}`,
488
option: (state) =>
489
`px-3 py-2 cursor-pointer ${
490
state.isSelected
491
? 'bg-blue-500 text-white'
492
: state.isFocused
493
? 'bg-blue-50 text-blue-900'
494
: 'text-gray-900 hover:bg-gray-50'
495
}`,
496
multiValue: () => 'bg-blue-100 text-blue-800 rounded-full px-2 py-1 text-sm',
497
multiValueRemove: () => 'ml-1 hover:bg-blue-200 rounded-full p-1',
498
};
499
500
const TailwindSelect = () => (
501
<Select
502
options={options}
503
classNames={tailwindClassNames}
504
classNamePrefix="react-select"
505
unstyled={true} // Remove default styles
506
/>
507
);
508
509
// CSS Modules integration
510
const cssModuleClassNames = {
511
container: () => styles.selectContainer,
512
control: (state) =>
513
`${styles.control} ${state.isFocused ? styles.controlFocused : ''}`,
514
option: (state) => {
515
let className = styles.option;
516
if (state.isSelected) className += ` ${styles.optionSelected}`;
517
if (state.isFocused) className += ` ${styles.optionFocused}`;
518
if (state.isDisabled) className += ` ${styles.optionDisabled}`;
519
return className;
520
},
521
menu: () => styles.menu,
522
menuList: () => styles.menuList,
523
};
524
525
// BEM methodology
526
const bemClassNames = {
527
container: () => 'select',
528
control: (state) =>
529
`select__control ${state.isFocused ? 'select__control--focused' : ''}`,
530
valueContainer: () => 'select__value-container',
531
input: () => 'select__input',
532
option: (state) => {
533
let classes = ['select__option'];
534
if (state.isSelected) classes.push('select__option--selected');
535
if (state.isFocused) classes.push('select__option--focused');
536
if (state.isDisabled) classes.push('select__option--disabled');
537
return classes.join(' ');
538
},
539
};
540
```
541
542
### Animated Components
543
544
Pre-built animated component variants for smooth transitions.
545
546
```typescript { .api }
547
/**
548
* Factory function for creating animated component variants
549
* @param externalComponents - Optional component overrides to animate
550
* @returns Object containing animated component variants
551
*/
552
function makeAnimated(
553
externalComponents?: Partial<SelectComponentsGeneric>
554
): Partial<SelectComponentsGeneric>;
555
556
/**
557
* Pre-configured animated components for common use cases
558
*/
559
const AnimatedComponents: {
560
Input: ComponentType<InputProps<any, boolean, GroupBase<any>>>;
561
MultiValue: ComponentType<MultiValueProps<any, boolean, GroupBase<any>>>;
562
Placeholder: ComponentType<PlaceholderProps<any, boolean, GroupBase<any>>>;
563
SingleValue: ComponentType<SingleValueProps<any, boolean, GroupBase<any>>>;
564
ValueContainer: ComponentType<ValueContainerProps<any, boolean, GroupBase<any>>>;
565
};
566
```
567
568
**Usage Examples:**
569
570
```typescript
571
import Select from "react-select";
572
import makeAnimated from "react-select/animated";
573
574
// Basic animated select
575
const AnimatedSelect = () => (
576
<Select
577
options={options}
578
components={makeAnimated()}
579
isMulti
580
/>
581
);
582
583
// Custom animated components
584
const customAnimatedComponents = makeAnimated({
585
MultiValue: CustomMultiValue, // Use animated version of custom component
586
});
587
588
const CustomAnimatedSelect = () => (
589
<Select
590
options={options}
591
components={customAnimatedComponents}
592
isMulti
593
/>
594
);
595
596
// Individual animated components
597
import { MultiValue, SingleValue } from "react-select/animated";
598
599
const MixedAnimatedSelect = ({ isMulti }) => (
600
<Select
601
options={options}
602
isMulti={isMulti}
603
components={{
604
MultiValue: isMulti ? MultiValue : undefined,
605
SingleValue: !isMulti ? SingleValue : undefined,
606
// Other components remain default (non-animated)
607
}}
608
/>
609
);
610
```
611
612
### Advanced Customization Patterns
613
614
```typescript
615
// Higher-order component for consistent styling
616
const withCustomStyling = (Component) => (props) => (
617
<Component
618
{...props}
619
styles={mergeStyles(brandStyles, props.styles || {})}
620
theme={brandTheme}
621
classNamePrefix="brand-select"
622
/>
623
);
624
625
const BrandSelect = withCustomStyling(Select);
626
const BrandAsyncSelect = withCustomStyling(AsyncSelect);
627
628
// Context-based theming
629
const ThemeContext = createContext(defaultTheme);
630
631
const ThemedSelect = (props) => {
632
const theme = useContext(ThemeContext);
633
return <Select {...props} theme={theme} />;
634
};
635
636
// Compound component pattern
637
const SelectGroup = ({ children, label, ...props }) => (
638
<div className="select-group">
639
{label && <label className="select-group__label">{label}</label>}
640
<Select {...props} />
641
{children}
642
</div>
643
);
644
645
SelectGroup.ErrorMessage = ({ children }) => (
646
<div className="select-group__error">{children}</div>
647
);
648
649
SelectGroup.HelpText = ({ children }) => (
650
<div className="select-group__help">{children}</div>
651
);
652
653
// Usage
654
const FormSelect = () => (
655
<SelectGroup label="Choose options">
656
<SelectGroup.HelpText>Select one or more options</SelectGroup.HelpText>
657
<SelectGroup.ErrorMessage>This field is required</SelectGroup.ErrorMessage>
658
</SelectGroup>
659
);
660
```