or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

async-data.mdcore-components.mdcustomization.mdhooks-state.mdindex.mdtypes-interfaces.md

customization.mddocs/

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

```