switch

A control that allows the user to toggle between checked and unchecked.

PreviousNext
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardSwitchComponent } from '@/shared/components/switch/switch.component';

@Component({
  selector: 'z-demo-switch-preview',
  imports: [ZardSwitchComponent],
  template: `
    <z-switch zId="airplane-mode">Airplane Mode</z-switch>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSwitchPreviewComponent {}

Installation

Copy
npx zard-cli@latest add switch

Usage

import { ZardSwitchComponent } from '@/shared/components/switch/switch.component';
Copy
<z-switch></z-switch>
Copy

Examples

description

Pair z-field-content with z-field-label and z-field-description for helper text.

Focus is shared across devices, and turns off when you leave the app.

import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardFieldImports } from '@/shared/components/field/field.imports';
import { ZardSwitchComponent } from '@/shared/components/switch/switch.component';

@Component({
  selector: 'z-demo-switch-description',
  imports: [...ZardFieldImports, ZardSwitchComponent],
  template: `
    <div z-field zOrientation="horizontal" class="max-w-sm">
      <div z-field-content>
        <label z-field-label for="switch-focus-mode">Share across devices</label>
        <p z-field-description>Focus is shared across devices, and turns off when you leave the app.</p>
      </div>
      <z-switch zId="switch-focus-mode" />
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSwitchDescriptionComponent {}

choice card

Card-style selection where label[z-field-label] wraps the entire z-field for a clickable card pattern.
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardFieldImports } from '@/shared/components/field/field.imports';
import { ZardSwitchComponent } from '@/shared/components/switch/switch.component';

@Component({
  selector: 'z-demo-switch-choice-card',
  imports: [...ZardFieldImports, ZardSwitchComponent],
  template: `
    <div z-field-group class="w-full min-w-sm">
      <label z-field-label for="switch-share">
        <div z-field zOrientation="horizontal">
          <div z-field-content>
            <div z-field-title>Share across devices</div>
            <p z-field-description>Focus is shared across devices, and turns off when you leave the app.</p>
          </div>
          <z-switch zId="switch-share"><span class="sr-only">Share across devices</span></z-switch>
        </div>
      </label>
      <label z-field-label for="switch-notifications">
        <div z-field zOrientation="horizontal">
          <div z-field-content>
            <div z-field-title>Enable notifications</div>
            <p z-field-description>Receive notifications when focus mode is enabled or disabled.</p>
          </div>
          <z-switch zId="switch-notifications" [zChecked]="true">
            <span class="sr-only">Enable notifications</span>
          </z-switch>
        </div>
      </label>
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSwitchChoiceCardComponent {}

disabled

Use zDisabled to disable the switch, and add data-disabled to z-field for the matching wrapper styles.
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardFieldImports } from '@/shared/components/field/field.imports';
import { ZardSwitchComponent } from '@/shared/components/switch/switch.component';

@Component({
  selector: 'z-demo-switch-disabled',
  imports: [...ZardFieldImports, ZardSwitchComponent],
  template: `
    <div z-field zOrientation="horizontal" data-disabled="true" class="w-fit">
      <z-switch zId="switch-disabled-unchecked" zDisabled />
      <label z-field-label for="switch-disabled-unchecked">Disabled</label>
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSwitchDisabledComponent {}

invalid

Use zInvalid to mark the switch as invalid (sets aria-invalid), and add data-invalid to z-field for the matching wrapper styles.

You must accept the terms and conditions to continue.

import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardFieldImports } from '@/shared/components/field/field.imports';
import { ZardSwitchComponent } from '@/shared/components/switch/switch.component';

@Component({
  selector: 'z-demo-switch-invalid',
  imports: [...ZardFieldImports, ZardSwitchComponent],
  template: `
    <div z-field zOrientation="horizontal" data-invalid="true" class="max-w-sm">
      <div z-field-content>
        <label z-field-label for="switch-terms">Accept terms and conditions</label>
        <p z-field-description>You must accept the terms and conditions to continue.</p>
      </div>
      <z-switch zId="switch-terms" zInvalid />
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSwitchInvalidComponent {}

size

Use zSize to change the size of the switch (default or sm).
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardSwitchComponent } from '@/shared/components/switch/switch.component';

@Component({
  selector: 'z-demo-switch-size',
  imports: [ZardSwitchComponent],
  template: `
    <div class="grid w-full min-w-sm gap-6">
      <div class="flex items-center gap-6">
        <z-switch zSize="sm">Small</z-switch>
        <z-switch zSize="sm" [zChecked]="true">Small (checked)</z-switch>
      </div>
      <div class="flex items-center gap-6">
        <z-switch>Default</z-switch>
        <z-switch [zChecked]="true">Default (checked)</z-switch>
      </div>
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSwitchSizeComponent {}

controlled

Drive the switch from outside with a one-way [zChecked] binding and the (zCheckedChange) output, instead of the two-way [(zChecked)] binding.

Notifications are on.

import { ChangeDetectionStrategy, Component, signal } from '@angular/core';

import { ZardButtonComponent } from '@/shared/components/button';
import { ZardFieldImports } from '@/shared/components/field/field.imports';
import { ZardSwitchComponent } from '@/shared/components/switch/switch.component';

@Component({
  selector: 'z-demo-switch-controlled',
  imports: [ZardSwitchComponent, ZardButtonComponent, ...ZardFieldImports],
  template: `
    <div z-field-group class="mx-auto w-64">
      <div z-field zOrientation="horizontal">
        <z-switch
          zId="notifications-controlled"
          [zChecked]="notifications()"
          (zCheckedChange)="notifications.set($event)"
        />
        <label z-field-label for="notifications-controlled">Enable notifications</label>
      </div>
      <p class="text-muted-foreground text-sm">Notifications are {{ notifications() ? 'on' : 'off' }}.</p>
      <button type="button" z-button zType="outline" zSize="sm" (click)="notifications.set(!notifications())">
        Toggle from outside
      </button>
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSwitchControlledComponent {
  protected readonly notifications = signal(true);
}

reactive forms

Bind z-switch with formControlName; a control created with disabled: true renders the switch disabled and keeps it in sync.

Newsletter: subscribed

import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';

import { ZardFieldImports } from '@/shared/components/field/field.imports';
import { ZardSwitchComponent } from '@/shared/components/switch/switch.component';

@Component({
  selector: 'z-demo-switch-reactive-forms',
  imports: [ZardSwitchComponent, ...ZardFieldImports, ReactiveFormsModule],
  template: `
    <form [formGroup]="form">
      <div z-field-group class="mx-auto w-64">
        <div z-field zOrientation="horizontal">
          <z-switch zId="newsletter-switch" formControlName="newsletter" />
          <label z-field-label for="newsletter-switch">Subscribe to newsletter</label>
        </div>
        <div z-field zOrientation="horizontal" data-disabled="true">
          <z-switch zId="beta-switch" formControlName="betaFeatures" />
          <label z-field-label for="beta-switch">Beta features (locked)</label>
        </div>
        <p class="text-muted-foreground text-sm">
          Newsletter: {{ form.controls.newsletter.value ? 'subscribed' : 'not subscribed' }}
        </p>
      </div>
    </form>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSwitchReactiveFormsComponent {
  private readonly fb = inject(FormBuilder);

  protected readonly form = this.fb.group({
    newsletter: [true],
    betaFeatures: [{ value: false, disabled: true }],
  });
}

rtl

To enable RTL support in zard/ui, see the RTL configuration guide.

تتم مشاركة التركيز عبر الأجهزة، ويتوقف عند مغادرة التطبيق.

// You will notice this example is rendered inside a wrapper that sets the dir and data-lang attributes, and
// reads its text from a translations helper. This is because this site is not RTL by default.
// In your application you won't need either: set dir="rtl" on <html> (or use the CDK Dir directive on a subtree)
// and write the text of your own language. See the RTL guide: https://zardui.com/docs/rtl

import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardFieldImports } from '@/shared/components/field/field.imports';
import { ZardSwitchComponent } from '@/shared/components/switch/switch.component';
import { injectRtlTranslation, type ZardRtlTranslations } from '@/shared/demo-utils/rtl-translation';

const translations: ZardRtlTranslations<{ label: string; description: string }> = {
  en: {
    dir: 'ltr',
    values: {
      label: 'Share across devices',
      description: 'Focus is shared across devices, and turns off when you leave the app.',
    },
  },
  ar: {
    dir: 'rtl',
    values: {
      label: 'المشاركة عبر الأجهزة',
      description: 'تتم مشاركة التركيز عبر الأجهزة، ويتوقف عند مغادرة التطبيق.',
    },
  },
  he: {
    dir: 'rtl',
    values: {
      label: 'שיתוף בין מכשירים',
      description: 'המיקוד משותף בין מכשירים, ומתבטל כשעוזבים את האפליקציה.',
    },
  },
};

@Component({
  selector: 'z-demo-switch-rtl',
  imports: [...ZardFieldImports, ZardSwitchComponent],
  template: `
    <div z-field zOrientation="horizontal" class="max-w-sm">
      <div z-field-content>
        <label z-field-label for="switch-rtl">{{ i18n.t().label }}</label>
        <p z-field-description>{{ i18n.t().description }}</p>
      </div>
      <z-switch zId="switch-rtl" />
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSwitchRtlComponent {
  protected readonly i18n = injectRtlTranslation(translations);
}

API Reference

z-switchComponent

A control that toggles between checked and unchecked, built on a native button with role="switch".

PropertyDescriptionTypeDefault
[class] Additional CSS classes ClassValue ''
[zChecked] Checked state, two-way bindable boolean false
[(zChecked)] Checked state (two-way binding) boolean false
[zId] Id applied to the underlying button string -
[zSize] Switch size 'default' | 'sm' 'default'
[zDisabled] Disables the switch boolean false
[zInvalid] Invalid state (sets aria-invalid) boolean false
github iconwhatsapp icondiscord iconX icon

Made with ❤ in Brazil. Open source and available on GitHub .