Skip to content

Switch

A toggle switch component for boolean states.

Import

ts
import { Description, Label, Switch, SwitchGroup } from '@heroui-vue/vue'

Usage

Basic

<template>
  <Switch>
    <Label class="text-sm">Enable notifications</Label>
  </Switch>
</template>

<script setup>
import { Switch, Label } from '@heroui-vue/vue'
</script>

Anatomy

vue
<template>
  <Switch>
    <span data-slot="switch-control">
      <span data-slot="switch-thumb">
        <span data-slot="switch-icon" />
      </span>
    </span>
    <div data-slot="switch-content">
      <Label />
      <Description />
    </div>
  </Switch>
</template>

Disabled

<template>
  <Switch is-disabled>
    <Label class="text-sm">Enable notifications</Label>
  </Switch>
</template>

<script setup>
import { Label, Switch } from '@heroui-vue/vue'
</script>

Default Selected

<template>
  <Switch default-selected>
    <Label class="text-sm">Enable notifications</Label>
  </Switch>
</template>

<script setup>
import { Label, Switch } from '@heroui-vue/vue'
</script>

Controlled

Switch is off

<template>
  <div class="flex flex-col gap-4">
    <Switch v-model="isSelected">
      <Label class="text-sm">Enable notifications</Label>
    </Switch>
    <p class="m-0 text-sm text-muted">Switch is {{ isSelected ? 'on' : 'off' }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { Label, Switch } from '@heroui-vue/vue'

const isSelected = ref(false)
</script>

Without Label

<template>
  <Switch aria-label="Enable notifications" />
</template>

<script setup>
import { Switch } from '@heroui-vue/vue'
</script>

Sizes

<template>
  <div class="flex gap-6">
    <Switch size="sm">
      <Label class="text-xs">Small</Label>
    </Switch>
    <Switch size="md">
      <Label class="text-sm">Medium</Label>
    </Switch>
    <Switch size="lg">
      <Label class="text-base">Large</Label>
    </Switch>
  </div>
</template>

<script setup>
import { Label, Switch } from '@heroui-vue/vue'
</script>

Label Position

<template>
  <div class="flex flex-col gap-4">
    <Switch>
      <Label class="text-sm">Label after</Label>
    </Switch>
    <Switch class="flex-row-reverse">
      <template #default="{ checked }">
        <Label class="text-sm">Label before {{ checked ? 'on' : 'off' }}</Label>
      </template>
    </Switch>
  </div>
</template>

<script setup>
import { Label, Switch } from '@heroui-vue/vue'
</script>

With Icons

<template>
  <div class="flex flex-wrap justify-center gap-3">
    <Switch default-selected size="lg">
      <template #icon="{ checked }">
        <svg v-if="checked" class="size-3 text-inherit opacity-100" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <path d="m20 6-11 11-5-5" />
        </svg>
        <svg v-else class="size-3 text-inherit opacity-70" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <path d="M12 2v10" />
          <path d="M18.4 6.6a9 9 0 1 1-12.8 0" />
        </svg>
      </template>
    </Switch>

    <Switch default-selected size="lg">
      <template #icon="{ checked }">
        <svg v-if="checked" class="size-3 text-inherit opacity-100" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <circle cx="12" cy="12" r="4" />
          <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41" />
        </svg>
        <svg v-else class="size-3 text-inherit opacity-70" viewBox="0 0 24 24" fill="currentColor">
          <path d="M21 14.5A8.5 8.5 0 0 1 9.5 3 9 9 0 1 0 21 14.5Z" />
        </svg>
      </template>
    </Switch>

    <Switch default-selected size="lg" style="--switch-control-bg-checked: rgb(239 68 68 / 80%); --switch-control-bg-checked-hover: rgb(220 38 38 / 85%);">
      <template #icon="{ checked }">
        <svg v-if="checked" class="size-3 text-inherit opacity-100" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <path d="M3 3l18 18" />
          <path d="M9 9v3a3 3 0 0 0 5.1 2.1" />
          <path d="M15 9.3V5a3 3 0 0 0-5.6-1.5" />
          <path d="M19 10v2a7 7 0 0 1-.8 3.3" />
          <path d="M5 10v2a7 7 0 0 0 10.5 6.1" />
        </svg>
        <svg v-else class="size-3 text-inherit opacity-70" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" />
          <path d="M19 10v2a7 7 0 0 1-14 0v-2" />
          <path d="M12 19v3" />
        </svg>
      </template>
    </Switch>

    <Switch default-selected size="lg" style="--switch-control-bg-checked: rgb(168 85 247 / 80%); --switch-control-bg-checked-hover: rgb(147 51 234 / 85%);">
      <template #icon="{ checked }">
        <svg v-if="checked" class="size-3 text-inherit opacity-100" viewBox="0 0 24 24" fill="currentColor">
          <path d="M18 8a6 6 0 0 0-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9Z" />
          <path d="M10 21h4" />
        </svg>
        <svg v-else class="size-3 text-inherit opacity-70" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <path d="m2 2 20 20" />
          <path d="M8.7 4.7A6 6 0 0 1 18 8c0 7 3 7 3 9H11" />
          <path d="M6 8c0 2.2-.3 3.7-.8 4.8" />
          <path d="M10 21h4" />
        </svg>
      </template>
    </Switch>

    <Switch default-selected size="lg" style="--switch-control-bg-checked: rgb(59 130 246 / 80%); --switch-control-bg-checked-hover: rgb(37 99 235 / 85%);">
      <template #icon="{ checked }">
        <svg v-if="checked" class="size-3 text-inherit opacity-100" viewBox="0 0 24 24" fill="currentColor">
          <path d="M11 5 6 9H2v6h4l5 4V5Z" />
          <path d="M15.5 8.5a5 5 0 0 1 0 7" />
          <path d="M18.5 5.5a9 9 0 0 1 0 13" />
        </svg>
        <svg v-else class="size-3 text-inherit opacity-70" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <path d="M11 5 6 9H2v6h4l5 4V5Z" />
          <path d="m22 9-6 6" />
          <path d="m16 9 6 6" />
        </svg>
      </template>
    </Switch>
  </div>
</template>

<script setup>
import { Switch } from '@heroui-vue/vue'
</script>

With Description

<template>
  <div class="max-w-sm text-left">
    <Switch>
      <Label class="text-sm">Public profile</Label>
      <Description>
        Allow others to see your profile information, recent activity, and public workspace
        membership across shared project pages.
      </Description>
    </Switch>
  </div>
</template>

<script setup>
import { Description, Label, Switch } from '@heroui-vue/vue'
</script>

Group

<template>
  <SwitchGroup>
    <Switch name="notifications">
      <Label class="text-sm">Allow Notifications</Label>
    </Switch>
    <Switch name="marketing">
      <Label class="text-sm">Marketing emails</Label>
    </Switch>
    <Switch name="social">
      <Label class="text-sm">Social media updates</Label>
    </Switch>
  </SwitchGroup>
</template>

<script setup>
import { Label, Switch, SwitchGroup } from '@heroui-vue/vue'
</script>

Group Horizontal

<template>
  <SwitchGroup class="overflow-x-auto" orientation="horizontal">
    <Switch name="notifications">
      <Label class="text-sm">Notifications</Label>
    </Switch>
    <Switch name="marketing">
      <Label class="text-sm">Marketing</Label>
    </Switch>
    <Switch name="social">
      <Label class="text-sm">Social</Label>
    </Switch>
  </SwitchGroup>
</template>

<script setup>
import { Label, Switch, SwitchGroup } from '@heroui-vue/vue'
</script>

Custom Styles

<template>
  <Switch
    default-selected
    style="--switch-control-bg: rgb(203 213 225); --switch-control-bg-hover: rgb(148 163 184); --switch-control-bg-checked: rgb(6 182 212); --switch-control-bg-checked-hover: rgb(8 145 178);"
  >
    <template #icon="{ checked }">
      <svg v-if="checked" class="size-3 text-inherit" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
        <path d="m20 6-11 11-5-5" />
      </svg>
      <svg v-else class="size-3 text-inherit opacity-70" viewBox="0 0 24 24" fill="currentColor">
        <path d="M21 14.5A8.5 8.5 0 0 1 9.5 3 9 9 0 1 0 21 14.5Z" />
      </svg>
    </template>
    <Label class="text-sm">Custom switch</Label>
  </Switch>
</template>

<script setup>
import { Label, Switch } from '@heroui-vue/vue'
</script>

Styling

CSS Classes

ClassDescription
.switchBase switch wrapper
.switch__controlTrack element
.switch__thumbThumb element
.switch__iconOptional thumb icon
.switch__contentLabel/description wrapper
.switch--smSmall size
.switch--mdMedium size
.switch--lgLarge size
.switch-groupGroup wrapper
.switch-group__itemsGroup item layout wrapper
.switch-group--horizontalHorizontal group orientation
.switch-group--verticalVertical group orientation

Interactive States

StateSelector
Selected[aria-checked="true"] / [data-selected="true"]
Hover[data-hovered="true"]
Pressed[data-pressed="true"]
Focus visible[data-focus-visible="true"]
Disabled[aria-disabled="true"] / [data-disabled="true"]
Invalid[data-invalid="true"]
Required[data-required="true"]

API

Switch Props

PropTypeDefaultDescription
v-modelbooleanundefinedControlled selected state
checkedbooleanundefinedControlled selected state alias
isSelectedbooleanundefinedReact-style controlled selected alias
defaultCheckedbooleanfalseInitial selected state
defaultSelectedbooleanundefinedReact-style initial selected alias
size'sm' | 'md' | 'lg''md'Switch size
disabledbooleanundefinedDisable the switch
isDisabledbooleanundefinedReact-style disabled alias
isInvalidbooleanundefinedInvalid state
requiredbooleanundefinedNative required state
isRequiredbooleanundefinedReact-style required alias
namestringundefinedForm name
valuestringundefinedForm value

Switch Events

EventPayloadDescription
update:modelValuebooleanEmitted when selected state changes
update:checkedbooleanEmitted when selected state changes
changebooleanEmitted when selected state changes

Switch Slots

SlotPropsDescription
default{ checked, isSelected }Label and description content
icon{ checked, isSelected }Optional thumb icon

SwitchGroup Props

PropTypeDefaultDescription
orientation'horizontal' | 'vertical'undefinedGroup orientation

Released under the Apache-2.0 License.