A lightweight & configurable timepicker directive
The easiest way to add the timepicker component to your app (will be added to the root module)
Basic
<timepicker [(ngModel)]="mytime"></timepicker>
<pre class="alert alert-info">Time is: {{mytime}}</pre>
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-basic',
templateUrl: './basic.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerBasicComponent {
mytime: Date = new Date();
}
Form
Time is: Wed Aug 12 2026 08:43:04 GMT+0000 (Coordinated Universal Time)
@if (form) {
<form [formGroup]="form">
<timepicker [formControlName]="'myControl'"></timepicker>
</form>
<br>
<button class="btn btn-success" (click)="form.get('myControl')?.enable()">Enable Control</button>
<button class="btn btn-warning" (click)="form.get('myControl')?.disable()">Disable Control</button>
<br><br>
<pre class="alert alert-info">Time is: {{ form.get('myControl')?.value }}</pre>
}
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-form',
templateUrl: './form.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerFormComponent {
form = new UntypedFormGroup({
myControl: new UntypedFormControl(new Date())
});
}
Meridian
<timepicker [(ngModel)]="mytime" [showMeridian]="ismeridian"></timepicker>
<pre class="alert alert-info">Time is: {{mytime}}</pre>
<br>
<button type="button" class="btn btn-info" (click)="toggleMode()">12H / 24H</button>
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-meridian',
templateUrl: './meridian.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerMeridianComponent {
ismeridian = true;
mytime: Date = new Date();
toggleMode(): void {
this.ismeridian = !this.ismeridian;
}
}
Custom meridian
Text in meridian labels can be customized by using meridians input property
<timepicker [(ngModel)]="mytime" [meridians]="meridians"></timepicker>
<pre class="alert alert-info">Time is: {{mytime}}</pre>
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-custom-meridian',
templateUrl: './custom-meridian.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerCustomMeridianComponent {
mytime: Date = new Date();
meridians = ['AM(Midnight to Noon)', 'PM(Noon to Midnight)'];
}
Min - Max
<timepicker [(ngModel)]="myTime" [min]="minTime" [max]="maxTime"></timepicker>
<pre class="alert alert-info">Time is: {{myTime}}</pre>
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-min-max',
templateUrl: './min-max.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerMinMaxComponent {
myTime: Date = new Date();
minTime: Date = new Date();
maxTime: Date = new Date();
constructor() {
this.minTime.setHours(8);
this.minTime.setMinutes(0);
this.maxTime.setHours(23);
this.maxTime.setMinutes(55);
}
}
Toggle minutes/seconds
| : | : | |||||
Time is: Wed Aug 12 2026 08:43:04 GMT+0000 (Coordinated Universal Time)
showMinutes: true
showSeconds: true
<timepicker [(ngModel)]="myTime" [showMinutes]="showMin" [showSeconds]="showSec"></timepicker>
<pre class="alert alert-info">Time is: {{myTime}}<br>showMinutes: {{showMin}}<br>showSeconds: {{showSec}}</pre>
<button class="btn btn-default text-center" (click)="toggleMinutes()">
{{showMin? 'Hide minutes' : 'Show minutes'}}
</button>
<button class="btn btn-default text-center" (click)="toggleSeconds()">
{{showSec? 'Hide seconds' : 'Show seconds'}}
</button>
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-seconds',
templateUrl: './toggle-minutes-seconds.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerToggleMinutesSecondsComponent {
myTime: Date = new Date();
showMin = true;
showSec = true;
toggleMinutes(): void {
this.showMin = !this.showMin;
}
toggleSeconds(): void {
this.showSec = !this.showSec;
}
}
Disabled
<timepicker [(ngModel)]="myTime" [showMeridian]="isMeridian" [disabled]="!isDisabled"></timepicker> <hr> <button type="button" class="btn btn-info" (click)="isDisabled=!isDisabled">Enable / Disable input</button>
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-disabled',
templateUrl: './disabled.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerDisabledComponent {
isMeridian = true;
isDisabled = true;
myTime = new Date();
}
Readonly
<timepicker [(ngModel)]="myTime" [showMeridian]="isMeridian" [readonlyInput]="!readonly"></timepicker> <hr> <button type="button" class="btn btn-info" (click)="readonly=!readonly">Editable / Readonly input</button>
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-readonly',
templateUrl: './readonly.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerReadonlyComponent {
isMeridian = false;
readonly = true;
myTime = new Date();
}
Custom steps
| : | : | |||||
Time is: Wed Aug 12 2026 08:43:04 GMT+0000 (Coordinated Universal Time)
<timepicker [(ngModel)]="mytime" [hourStep]="hstep" [minuteStep]="mstep" [showSeconds]="true" [secondsStep]="sstep"></timepicker>
<pre class="alert alert-info">Time is: {{mytime}}</pre>
<div class="row">
<div class="col-xs-6 col-6 col-md-3">
Hours step is:
<select class="form-control" [(ngModel)]="hstep">
@for (opt of options.hstep; track opt) {
<option [value]="opt">{{opt}}</option>
}
</select>
</div>
<div class="col-xs-6 col-6 col-md-3">
Minutes step is:
<select class="form-control" [(ngModel)]="mstep">
@for (opt of options.mstep; track opt) {
<option [value]="opt">{{opt}}</option>
}
</select>
</div>
<div class="col-xs-6 col-6 col-md-3">
Seconds step is:
<select class="form-control" [(ngModel)]="sstep">
@for (opt of options.sstep; track opt) {
<option [value]="opt">{{opt}}</option>
}
</select>
</div>
</div>
import { Component, ChangeDetectionStrategy } from '@angular/core';
interface IOptions {
hstep: number[];
mstep: number[];
sstep: number[];
}
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-custom',
templateUrl: './custom.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerCustomComponent {
hstep = 1;
mstep = 15;
sstep = 10;
mytime: Date = new Date();
options: IOptions = {
hstep: [1, 2, 3],
mstep: [1, 5, 10, 15, 25, 30],
sstep: [5, 10, 20, 30]
};
}
Custom validation
<p>Illustrates custom validation, you have to select time between 11:00 and 12:59</p>
<div class="form-group mb-3">
<timepicker [(ngModel)]="myTime" [formControl]="ctrl" required></timepicker>
</div>
<pre class="alert"
[class.alert-danger]="!ctrl.valid && !ctrl.pristine"
[class.alert-success]="(ctrl.valid && !ctrl.pristine) || ctrl.value === null">
Time is: {{myTime}}
</pre>
@if (ctrl.errors && ctrl.errors['outOfRange']) {
<div class="alert alert-danger">Invalid time</div>
}
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { AbstractControl, UntypedFormControl } from '@angular/forms';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-custom-validation',
templateUrl: './custom-validation.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerCustomValidationComponent {
myTime?: Date;
ctrl = new UntypedFormControl('', (control: AbstractControl) => {
const value = control.value;
if (!value) {
return null;
}
const hours = value.getHours();
if (hours < 11 || hours > 12) {
return { outOfRange: true };
}
return null;
});
}
Custom validation with isValid event
isValid event emits true if a value is a valid data.
Enter an invalid data to see error
<timepicker [(ngModel)]="myTime" [showMeridian]="isMeridian" (isValid)="isValid($event)"></timepicker>
<hr>
<pre class="alert"
[class.alert-danger]="!valid"
[class.alert-success]="valid">
Time is: {{myTime}}
</pre>
@if (!valid) {
<div class="alert alert-danger">Invalid time</div>
}
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-isvalid',
templateUrl: './isvalid.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerIsValidComponent {
isMeridian = true;
myTime = new Date();
valid = true;
isValid(event: boolean): void {
this.valid = event;
}
}
Dynamic
<timepicker [(ngModel)]="mytime" (ngModelChange)="changed()" (isValid)="isValid = $event"></timepicker>
<pre class="alert alert-info">Time is: {{mytime}}</pre>
@if (!isValid) {
<pre class="alert alert-danger">Invalid time format</pre>
}
<button type="button" class="btn btn-primary" (click)="update()">Set to 14:00</button>
<button type="button" class="btn btn-danger" (click)="clear()">Clear</button>
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { TimepickerConfig } from 'ngx-bootstrap/timepicker';
export function getTimepickerConfig(): TimepickerConfig {
return Object.assign(new TimepickerConfig(), {
allowEmptyTime: true
});
}
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-dynamic',
templateUrl: './dynamic.html',
providers: [{ provide: TimepickerConfig, useFactory: getTimepickerConfig }],
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerDynamicComponent {
mytime: Date | undefined = new Date();
isValid?: boolean;
update(): void {
const time = new Date();
time.setHours(14);
time.setMinutes(0);
this.mytime = time;
}
changed(): void {
console.log(`Time changed to: ${this.mytime}`);
}
clear(): void {
this.mytime = void 0;
}
}
Mouse wheel
<timepicker [(ngModel)]="myTime" [mousewheel]="allowMouseWheel"></timepicker>
<hr>
<button type="button" class="btn btn-info section bd-example" (click)="allowMouseWheel = !allowMouseWheel">Enable / Disable mouse wheel</button>
<pre class="alert alert-info">Time is: {{myTime}}</pre>
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-mousewheel',
templateUrl: './mousewheel.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerMousewheelComponent {
allowMouseWheel = true;
myTime = new Date();
}
Empty date
<timepicker [(ngModel)]="myTime" (isValid)="isValid = $event"></timepicker>
<hr>
<pre class="alert alert-info">Time is: {{myTime}}</pre>
@if (!isValid) {
<pre class="alert alert-danger">Invalid time format</pre>
}
<button type="button" class="btn btn-danger" (click)="clear()">Clear</button>
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { TimepickerConfig } from 'ngx-bootstrap/timepicker';
export function getTimepickerConfig(): TimepickerConfig {
return Object.assign(new TimepickerConfig(), {
allowEmptyTime: true
});
}
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-empty-date',
templateUrl: './empty-date.html',
providers: [{ provide: TimepickerConfig, useFactory: getTimepickerConfig }],
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerEmptyDateComponent {
allowEmptyTime = true;
myTime?: Date = new Date();
isValid?: boolean;
clear(): void {
this.myTime = void 0;
}
}
Arrow keys
<timepicker [(ngModel)]="myTime" [arrowkeys]="allowArrowKeys"></timepicker>
<hr>
<button type="button" class="btn btn-info section bd-example" (click)="allowArrowKeys = !allowArrowKeys">Enable / Disable keyboard arrow keys</button>
<pre class="alert alert-info">Time is: {{myTime}}</pre>
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-arrowkeys',
templateUrl: './arrowkeys.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerArrowkeysComponent {
allowArrowKeys = true;
myTime = new Date();
}
Spinners
<timepicker [(ngModel)]="myTime" [showMeridian]="isMeridian" [showSpinners]="showSpinners"></timepicker> <hr> <button type="button" class="btn btn-info" (click)="showSpinners = !showSpinners">Show / Hide spinners</button>
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-spinners',
templateUrl: './spinners.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerSpinnersComponent {
isMeridian = false;
showSpinners = true;
myTime: Date = new Date();
}
Placeholder
<timepicker [hoursPlaceholder]="hoursPlaceholder"
[minutesPlaceholder]="minutesPlaceholder"
[secondsPlaceholder]="secondsPlaceholder" [showSeconds]="true"></timepicker>
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-placeholder',
templateUrl: './placeholder.html',
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerPlaceholderComponent {
hoursPlaceholder = 'hh';
minutesPlaceholder = 'mm';
secondsPlaceholder = 'ss';
}
Configuring defaults
<timepicker [(ngModel)]="mytime"></timepicker>
<pre class="alert alert-info">Time is: {{mytime}}</pre>
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { TimepickerConfig } from 'ngx-bootstrap/timepicker';
// such override allows to keep some initial values
export function getTimepickerConfig(): TimepickerConfig {
return Object.assign(new TimepickerConfig(), {
hourStep: 2,
minuteStep: 10,
showMeridian: false,
readonlyInput: false,
mousewheel: true,
showMinutes: true,
showSeconds: false,
labelHours: 'Hours',
labelMinutes: 'Minutes',
labelSeconds: 'Seconds'
});
}
@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'demo-timepicker-config',
templateUrl: './config.html',
providers: [{ provide: TimepickerConfig, useFactory: getTimepickerConfig }],
changeDetection: ChangeDetectionStrategy.Eager,
standalone: false
})
export class DemoTimepickerConfigComponent {
mytime?: string;
}
Installation
ng add ngx-bootstrap --component timepicker
### Standalone component usage
import { TimepickerModule } from 'ngx-bootstrap/timepicker';
@Component({
standalone: true,
imports: [TimepickerModule,...]
})
export class AppComponent(){}
### Module usage
import { TimepickerModule } from 'ngx-bootstrap/timepicker';
@NgModule({
imports: [TimepickerModule,...]
})
export class AppModule(){}
TimepickerConfig
Provides default configuration values for timepicker
Properties
allowEmptyTime | Type: booleanDefault value: falseif true emptyTime is not marked as invalid |
ariaLabelHours | Type: stringDefault value: hourshours aria label |
ariaLabelMinutes | Type: stringDefault value: minutesminutes aria label |
ariaLabelSeconds | Type: stringDefault value: secondsseconds aria label |
arrowkeys | Type: booleanDefault value: trueif true the values of hours and minutes can be changed using the up/down arrow keys on the keyboard |
disabled | Type: booleanDefault value: falseif true hours and minutes fields will be disabled |
hoursPlaceholder | Type: stringDefault value: HHplaceholder for hours field in timepicker |
hourStep | Type: numberDefault value: 1hours change step |
max | Type: Date | undefinedmaximum time user can select |
meridians | Type: string[]meridian labels based on locale |
min | Type: Date | undefinedminimum time user can select |
minutesPlaceholder | Type: stringDefault value: MMplaceholder for minutes field in timepicker |
minuteStep | Type: numberDefault value: 5minutes change step |
mousewheel | Type: booleanDefault value: trueif true scroll inside hours and minutes inputs will change time |
readonlyInput | Type: booleanDefault value: falseif true hours and minutes fields will be readonly |
secondsPlaceholder | Type: stringDefault value: SSplaceholder for seconds field in timepicker |
secondsStep | Type: numberDefault value: 10seconds changes step |
showMeridian | Type: booleanDefault value: trueif true works in 12H mode and displays AM/PM. If false works in 24H mode and hides AM/PM |
showMinutes | Type: booleanDefault value: trueshow minutes in timepicker |
showSeconds | Type: booleanDefault value: falseshow seconds in timepicker |
showSpinners | Type: booleanDefault value: trueif true spinner arrows above and below the inputs will be shown |
Form
Time is: Wed Aug 12 2026 08:43:04 GMT+0000 (Coordinated Universal Time)
Toggle minutes/seconds
| : | : | |||||
Time is: Wed Aug 12 2026 08:43:04 GMT+0000 (Coordinated Universal Time)
showMinutes: true
showSeconds: true
Custom steps
| : | : | |||||
Time is: Wed Aug 12 2026 08:43:04 GMT+0000 (Coordinated Universal Time)
