Frappe UIFrappe UI

Switch

A toggle input for turning options on or off. Clearly indicates state changes and allows quick, intuitive control.

Playground

label
description
size
controlPosition
padded
required
error
disabled
<Switch
  label="Notifications"
  v-model="value"
/>

With icon

Strings starting with lucide- route through the shared Lucide Tailwind utility. Component values are rendered with <component :is>.

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>

States

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

<template>
  <div class="flex flex-col gap-3 items-start">
    <Switch label="Default" />
    <Switch label="Required" required />
    <Switch label="Disabled" disabled />
    <Switch label="With error" error="You must enable this to continue." />
  </div>
</template>

In a toolbar

The padded prop gives the switch a clickable surface and hover state that matches the buttons beside it, so it sits comfortably in a toolbar. Clicking 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>

Settings list

When a description is present the switch sits on the right of the row. The switch control stays interactive on its own — there are no row-level hover or focus states.

Underline misspelled words as you type.

Automatically fix common spelling mistakes.

Show predictive writing suggestions as you compose emails.

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

const writing = ref({
  spelling: true,
  autocorrect: true,
  smartCompose: false,
})
</script>

<template>
  <div class="w-[420px] divide-y divide-outline-gray-1">
    <Switch
      v-model="writing.spelling"
      label="Spelling suggestions"
      description="Underline misspelled words as you type."
      class="w-full py-2.5"
    />
    <Switch
      v-model="writing.autocorrect"
      label="Autocorrect"
      description="Automatically fix common spelling mistakes."
      class="w-full py-2.5"
    />
    <Switch
      v-model="writing.smartCompose"
      label="Smart compose"
      description="Show predictive writing suggestions as you compose emails."
      class="w-full py-2.5"
    />
  </div>
</template>

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
string | FrappeUIError

Error message rendered below the input. When set, the control receives `aria-invalid="true"` and `data-state="invalid"`. May be either a string or an `Error` object whose `messages?: string[]` is rendered as stacked lines (with `Error.message` as the fallback).

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.