Switch ​

A toggle that turns a setting on or off.

label
description
size
controlPosition
<Switch
  label="Notifications"
  v-model="value"
/>

Examples ​

Notification settings ​

icon shows an icon before the label.

A weekly summary delivered every Friday.

vue
<script setup lang="ts">
import { Switch } from 'frappe-ui'
</script>

<template>
  <div class="flex flex-col gap-3 w-80">
    <Switch icon="lucide-bell" label="Notifications" class="w-full" />
    <Switch
      icon="lucide-mail"
      label="Email digest"
      description="A weekly summary delivered every Friday."
      class="w-full"
    />
  </div>
</template>

Toolbar toggle ​

padded gives the switch a clickable surface and a hover state that match the buttons beside it. A click anywhere on the row toggles the switch.

vue
<script setup lang="ts">
import { ref } from 'vue'
import { Button, Switch } from 'frappe-ui'

const showActivity = ref(true)
</script>

<template>
  <div class="flex items-center gap-2">
    <Switch v-model="showActivity" padded label="Show activity" />
    <Button variant="subtle" icon-left="lucide-settings-2" label="Customize" />
    <Button variant="solid" icon-right="lucide-chevron-down" label="New" />
  </div>
</template>

Behavior ​

Icon ​

icon takes a lucide- class name, such as "lucide-bell", or a component, which is rendered with <component :is>.

Control position ​

controlPosition is end by default, so the switch sits after the label and the row fills the available width. start puts the switch before the label, and the row fits its content. Both follow the text direction.

Padded ​

padded wraps the switch and the label in a clickable surface with hover, active and focus states, for toolbars and menu items. Without padded, only the switch and its label respond to clicks, and the row has no hover or focus state.

Label, description and error ​

label renders beside the switch and description below it. error renders below the row and hides description. It takes a string, an array of strings (one line each), or an Error, the same values as ErrorMessage. An empty string or an empty array means no error. required adds a red asterisk to the label.

The #label slot replaces the label text and the required marker, and receives { required }. A #description slot is not hidden by error. It renders above the error.

Attributes ​

class and style go to the layout wrapper. Everything else (name, aria-*, data-* and listeners) goes once to the interactive element, which for Switch is the switch button.

Accessibility ​

Switch renders a button with role="switch", and Space or Enter toggles it. An icon given as a class name is hidden from screen readers. required sets aria-required. While error is set, the switch gets aria-invalid and an aria-errormessage that points to the error text.

API Reference ​

Show types
typescript
import type { Component } from 'vue'
import type { ToggleSize } from '../../composables/inputTypes'
import type { InputLabelingProps } from '../../composables/useInputLabeling'

export interface SwitchProps extends InputLabelingProps {
  /** Size of the switch control */
  size?: ToggleSize

  /** Wraps the control and label in a clickable surface with hover, active and focus states — useful for settings rows and menu items. */
  padded?: boolean

  /** Position of the control relative to the label, along the inline axis (RTL-aware). `start` is the leading side, `end` the trailing side. Defaults to `end`, so the switch trails the label. */
  controlPosition?: 'start' | 'end'

  /** Disables the switch and prevents interaction */
  disabled?: boolean

  /** Optional icon rendered alongside the label. Strings starting with `lucide-` are rendered via the shared Lucide Tailwind utility; component values are rendered with `<component :is>`. */
  icon?: string | Component
}
size
= "sm"
ToggleSize

Size of the switch control

padded
= false
boolean

Wraps the control and label in a clickable surface with hover, active and focus states — useful for settings rows and menu items.

controlPosition
"start" | "end"

Position of the control relative to the label, along the inline axis (RTL-aware). `start` is the leading side, `end` the trailing side. Defaults to `end`, so the switch trails the label.

disabled
= false
boolean

Disables the switch and prevents interaction

icon
string | Component

Optional icon rendered alongside the label. Strings starting with `lucide-` are rendered via the shared Lucide Tailwind utility; component values are rendered with `<component :is>`.

label
string

Label rendered above (or beside, for binary controls) the input.

description
string

Helper text rendered below the input. Hidden when `error` is set. A `#description` slot is not: it renders beside the error, and is referenced alongside it.

error
ErrorMessageValue

Error message rendered below the input. When set, the control receives `aria-invalid="true"` and `data-state="invalid"`. Takes a string, an array of strings, or an `Error` whose `messages` are rendered as stacked lines (with `Error.message` as the fallback). This is the same value `ErrorMessage.message` takes. An empty array and an empty string both mean no error.

required
boolean

Marks the field as required. Renders an asterisk next to the label, with `sr-only` text that announces it, and forwards `required` / `aria-required` to the underlying control where the control's role allows it. `data-required` is set either way.

id
string

HTML id of the underlying control. Auto-generated via `useId()` if omitted.

modelValue
= false
boolean
label
{ required: boolean; }

Overrides the rendered label content. Receives `{ required }`.

description
—

Overrides the rendered description content.

update:modelValue
[value: boolean]

Fired when the model value changes.

focus
(options?: FocusOptions) => void

Moves focus to the switch.