Base UI (Angular) select is a custom listbox, not a styled native select. Install custom-select with the CLI, bind ngModel, formControlName, or Angular 22 [formField], and project ng-template baseSelectOption when a row needs more than a label.
Install with npx base-ui-cli add custom-select.
Playground
<base-custom-select [options]="cars" displayKey="label" valueKey="value" [(ngModel)]="selectedCar" placeholder="Choose a car…"> </base-custom-select>
Use <option> tag inside <base-select> component and you'll get the expected result. As usual the first element works as a placeholder. See example before
<base-select>
<option value="0">Select Car</option>
<option value="1">BMW</option>
<option value="2">WV</option>
<option value="2">Opel</option>
<option value="2">Audi</option>
</base-select>
TypeScript
import { Component } from '@angular/core';
// Install: npx base-ui-cli add custom-select
// Paths are relative to aliases.components (default: src/app/components)
import { CustomSelectOptionContext, CustomSelectOptionDirective } from './custom-select/custom-select-option.directive';
import { CustomSelectComponent } from './custom-select/custom-select.component';
@Component({
selector: 'app-custom-select-example',
imports: [
CustomSelectComponent,
CustomSelectOptionDirective
],
template: `...`
})
export class CustomSelectExampleComponent {}Install
npx base-ui-cli add custom-select
You can use the placeholder attribute to show a descriptive text when no option is selected.
<base-select placeholder="Choose your car...">
<option value="1">BMW</option>
<option value="2">WV</option>
<option value="3">Opel</option>
<option value="4">Audi</option>
</base-select>
TypeScript
import { Component } from '@angular/core';
// Install: npx base-ui-cli add custom-select
// Paths are relative to aliases.components (default: src/app/components)
import { CustomSelectOptionContext, CustomSelectOptionDirective } from './custom-select/custom-select-option.directive';
import { CustomSelectComponent } from './custom-select/custom-select.component';
@Component({
selector: 'app-custom-select-example',
imports: [
CustomSelectComponent,
CustomSelectOptionDirective
],
template: `...`
})
export class CustomSelectExampleComponent {}Install
npx base-ui-cli add custom-select
Use [disabled]="true" inside <base-select> tag to disable that select.
<base-select disabled="true">
<option value="0">Select Car</option>
<option value="1">BMW</option>
<option value="2">WV</option>
<option value="2">Opel</option>
<option value="2">Audi</option>
</base-select>
TypeScript
import { Component } from '@angular/core';
// Install: npx base-ui-cli add custom-select
// Paths are relative to aliases.components (default: src/app/components)
import { CustomSelectOptionContext, CustomSelectOptionDirective } from './custom-select/custom-select-option.directive';
import { CustomSelectComponent } from './custom-select/custom-select.component';
@Component({
selector: 'app-custom-select-example',
imports: [
CustomSelectComponent,
CustomSelectOptionDirective
],
template: `...`
})
export class CustomSelectExampleComponent {}Install
npx base-ui-cli add custom-select
The <base-custom-select> is a non-native styling component that provides more control over the appearance.
<base-custom-select label="Select your favorite car" placeholder="Choose a car..." [options]="cars" displayKey="label" valueKey="value" [(ngModel)]="selectedCar" ></base-custom-select>
TypeScript
import { Component } from '@angular/core';
// Install: npx base-ui-cli add custom-select
// Paths are relative to aliases.components (default: src/app/components)
import { CustomSelectOptionContext, CustomSelectOptionDirective } from './custom-select/custom-select-option.directive';
import { CustomSelectComponent } from './custom-select/custom-select.component';
@Component({
selector: 'app-custom-select-example',
imports: [
CustomSelectComponent,
CustomSelectOptionDirective
],
template: `...`
})
export class CustomSelectExampleComponent {}Install
npx base-ui-cli add custom-select
Project ng-template baseSelectOption to customize listbox rows. Import CustomSelectOptionDirective. Context: let-option, let-label="label", let-selected="selected".
<base-custom-select [options]="cars" displayKey="label" valueKey="value" [(ngModel)]="selectedCar">
<ng-template baseSelectOption let-label="label">
<span>BMW</span>
</ng-template>
</base-custom-select>
TypeScript
import { Component } from '@angular/core';
// Install: npx base-ui-cli add custom-select
// Paths are relative to aliases.components (default: src/app/components)
import { CustomSelectOptionContext, CustomSelectOptionDirective } from './custom-select/custom-select-option.directive';
import { CustomSelectComponent } from './custom-select/custom-select.component';
@Component({
selector: 'app-custom-select-example',
imports: [
CustomSelectComponent,
CustomSelectOptionDirective
],
template: `...`
})
export class CustomSelectExampleComponent {}Install
npx base-ui-cli add custom-select
Use the size property to change the height of the select component. Available sizes match our standard inputs: 'sm' | 'default' | 'xl'.
<base-select size="sm" placeholder="Small Select">
<option value="1">Option 1</option>
</base-select>
TypeScript
import { Component } from '@angular/core';
// Install: npx base-ui-cli add custom-select
// Paths are relative to aliases.components (default: src/app/components)
import { CustomSelectOptionContext, CustomSelectOptionDirective } from './custom-select/custom-select-option.directive';
import { CustomSelectComponent } from './custom-select/custom-select.component';
@Component({
selector: 'app-custom-select-example',
imports: [
CustomSelectComponent,
CustomSelectOptionDirective
],
template: `...`
})
export class CustomSelectExampleComponent {}Install
npx base-ui-cli add custom-select
Disabled is a native attribute. Empty options still show the trigger. Keep validation copy next to the control — do not nest base-custom-select in base-input-group.
TypeScript
import { Component } from '@angular/core';
// Install: npx base-ui-cli add custom-select
// Paths are relative to aliases.components (default: src/app/components)
import { CustomSelectOptionContext, CustomSelectOptionDirective } from './custom-select/custom-select-option.directive';
import { CustomSelectComponent } from './custom-select/custom-select.component';
@Component({
selector: 'app-custom-select-example',
imports: [
CustomSelectComponent,
CustomSelectOptionDirective
],
template: `...`
})
export class CustomSelectExampleComponent {}Install
npx base-ui-cli add custom-select
base-custom-select is a CVA control. Bind Angular 22 [formField] or formControlName / [(ngModel)] on the host — not inside an input-group.
<base-custom-select [options]="cars" displayKey="label" valueKey="value" [formField]="carForm.car"> </base-custom-select>
Native inputs take [formField] directly. CVA hosts (base-toggle, base-custom-select, combobox) use the same binding via Angular’s CVA interop. Full recipe: signal forms cookbook.
TypeScript
import { Component } from '@angular/core';
// Install: npx base-ui-cli add custom-select
// Paths are relative to aliases.components (default: src/app/components)
import { CustomSelectOptionContext, CustomSelectOptionDirective } from './custom-select/custom-select-option.directive';
import { CustomSelectComponent } from './custom-select/custom-select.component';
@Component({
selector: 'app-custom-select-example',
imports: [
CustomSelectComponent,
CustomSelectOptionDirective
],
template: `...`
})
export class CustomSelectExampleComponent {}Install
npx base-ui-cli add custom-select
Site-wide VPAT-style notes live on the accessibility report.
Unit tests: copy CustomSelectHarness with custom-select and load it with TestbedHarnessEnvironment.loader(fixture). See Getting started — Testing.
Run the following command to add this component to your project:
npx base-ui-cli add custom-select npx base-ui-cli add select
For AI agents
Copy a prompt with the registry name, CLI install, and import — or add the Base UI MCP server.
npx -y base-ui-ng-mcp
Base UI provides standalone components. Import the exact elements you want to use into your component.
// Install: npx base-ui-cli add custom-select
// Paths are relative to aliases.components (default: src/app/components)
import { CustomSelectOptionContext, CustomSelectOptionDirective } from './custom-select/custom-select-option.directive';
import { CustomSelectComponent } from './custom-select/custom-select.component';
@Component({
selector: 'app-your-component',
imports: [
CustomSelectComponent,
CustomSelectOptionDirective
],
template: `...`
})
export class YourComponent {} API for custom-select — generated from JSDoc in the library source.
| API | Member | Type | Default | Description |
|---|---|---|---|---|
CustomSelectOptionContext | $implicit | unknown | — | — |
CustomSelectOptionContext | option | unknown | — | — |
CustomSelectOptionContext | label | unknown | — | — |
CustomSelectOptionContext | selected | boolean | — | — |
CustomSelectOptionContext | active | boolean | — | — |
CustomSelectOptionContext | index | number | — | — |
base-custom-select | class | string | '' | Additional host CSS classes (merged via cn()). |
base-custom-select | label | string | '' | — |
base-custom-select | placeholder | unknown | 'Select an option' | — |
base-custom-select | options | unknown[] | [] | — |
base-custom-select | displayKey | unknown | 'label' | — |
base-custom-select | valueKey | unknown | 'value' | — |
base-custom-select | multiple | boolean | false | — |
base-custom-select | (selectionChange) | void | — | Emits when selectionChange occurs. |
[baseSelectOption] | — | — | — | Marks an `ng-template` as the option renderer for . When omitted, the listbox shows `displayKey` (or the option itself). |
Content