GitHub35X

ActionSheet

A bottom sheet dialog for actions with a promise-based selection API

PreviousNext
      
      <script setup lang="ts">
import { ActionSheet, ActionSheetCancel, ActionSheetContent, ActionSheetGroup, ActionSheetOption, ActionSheetTrigger } from "@/components/action-sheet";
import { Button } from "@/components/ui/button";
import { toast } from "@/components/ui/sonner";
import { ref, useTemplateRef } from "vue";

const isOpen = ref<boolean>(false);
const actionSheet = useTemplateRef<InstanceType<typeof ActionSheet>>("actionSheet");

async function openActionSheet() {
  const result = await actionSheet.value?.start<string>();

  if (!result) {
    return;
  }

  if (result?.cancelled) {
    toast(`Action sheet cancelled: ${result.cancelledReason}`);
  } else {
    toast(`Selected option: ${result.selectedOption}`);
  }
}
</script>

<template>
  <div class="flex flex-col gap-6">
    <Button variant="outline" @click="openActionSheet()">
      Open with promise
    </Button>

    <ActionSheet v-model:open="isOpen" ref="actionSheet">
      <ActionSheetTrigger>
        <Button variant="outline">Open with trigger</Button>
      </ActionSheetTrigger>

      <ActionSheetContent>
        <ActionSheetGroup>
          <ActionSheetOption value="option1">
            Option 1
          </ActionSheetOption>
          <ActionSheetOption value="option2">
            Option 2
          </ActionSheetOption>
          <ActionSheetOption value="option3">
            Option 3
          </ActionSheetOption>
          <ActionSheetOption value="option4">
            Option 4
          </ActionSheetOption>
        </ActionSheetGroup>

        <ActionSheetGroup>
          <ActionSheetOption value="more-a">
            More A
          </ActionSheetOption>
          <ActionSheetOption value="more-b">
            More B
          </ActionSheetOption>
        </ActionSheetGroup>

        <ActionSheetCancel>Cancel</ActionSheetCancel>
      </ActionSheetContent>
    </ActionSheet>
  </div>
</template>
    

Features

  • Promise-based API — Call start() on ActionSheet and await a typed result (ActionSheetStartResult) when the user picks an option, cancels, or dismisses the sheet
  • Classic usage — Control visibility with v-model:open and open via ActionSheetTrigger
  • Composable layout — Stack multiple ActionSheetGroup blocks for separated groups of actions

Installation

Install from the Vuzeno registry with the shadcn-vue CLI:

      
      bunx --bun shadcn-vue@latest add https://vuzeno.com/r/action-sheet.json
    

Examples

Classic action sheet

Use ActionSheetTrigger to open the sheet and ActionSheetOption for each choice. ActionSheetCancel fires the cancel path (and closes the sheet).

      
      <script setup lang="ts">
import {
  ActionSheet,
  ActionSheetCancel,
  ActionSheetContent,
  ActionSheetOption,
  ActionSheetGroup,
  ActionSheetTrigger,
} from "@vuzeno/registry/ui/action-sheet";
import { Button } from "@/components/button";
import { ref } from "vue";

const isOpen = ref(false);
</script>

<template>
  <ActionSheet v-model:open="isOpen">
    <ActionSheetTrigger>
      <Button>Open Action Sheet</Button>
    </ActionSheetTrigger>

    <ActionSheetContent>
      <ActionSheetGroup>
        <ActionSheetOption value="option1">Option 1</ActionSheetOption>
        <ActionSheetOption value="option2">Option 2</ActionSheetOption>
        <ActionSheetOption value="option3">Option 3</ActionSheetOption>
        <ActionSheetOption value="option4">Option 4</ActionSheetOption>
      </ActionSheetGroup>

      <ActionSheetCancel>Cancel</ActionSheetCancel>
    </ActionSheetContent>
  </ActionSheet>
</template>
    

Promise API

Expose the root with ref and useTemplateRef, then call start(). The promise resolves when the user selects an option, taps cancel, or closes the overlay (when closeOnClickOutside is enabled).

      
      <script setup lang="ts">
import {
  ActionSheet,
  ActionSheetCancel,
  ActionSheetContent,
  ActionSheetOption,
  ActionSheetGroup,
  ActionSheetTrigger,
} from "@vuzeno/registry/ui/action-sheet";
import { Button } from "@/components/button";
import { ref, useTemplateRef } from "vue";

const isOpen = ref(false);
const actionSheet = useTemplateRef<InstanceType<typeof ActionSheet>>("actionSheet");

async function openActionSheet() {
  const result = await actionSheet.value?.start<string>();

  if (!result) {
    return;
  }

  if (result.cancelled) {
    // result.cancelledReason is "cancel" | "close"
  } else {
    // result.selectedOption is the chosen value
  }
}
</script>

<template>
  <div class="flex flex-col gap-4">
    <Button @click="openActionSheet()">Open Action Sheet #1</Button>
    <ActionSheet v-model:open="isOpen" ref="actionSheet">
      <ActionSheetTrigger>
        <Button>Open Action Sheet #2</Button>
      </ActionSheetTrigger>

      <ActionSheetContent>
        <ActionSheetGroup>
          <ActionSheetOption value="option1">Option 1</ActionSheetOption>
          <ActionSheetOption value="option2">Option 2</ActionSheetOption>
          <ActionSheetOption value="option3">Option 3</ActionSheetOption>
          <ActionSheetOption value="option4">Option 4</ActionSheetOption>
        </ActionSheetGroup>

        <ActionSheetCancel>Cancel</ActionSheetCancel>
      </ActionSheetContent>
    </ActionSheet>
  </div>
</template>
    

Multiple option groups

Compose several ActionSheetGroup components inside ActionSheetContent to separate primary actions from secondary ones. Spacing between groups is handled by ActionSheetContent.

      
      <template>
  <ActionSheet v-model:open="isOpen">
    <ActionSheetTrigger>
      <Button>Open</Button>
    </ActionSheetTrigger>

    <ActionSheetContent>
      <ActionSheetGroup>
        <ActionSheetOption value="edit">Edit</ActionSheetOption>
        <ActionSheetOption value="duplicate">Duplicate</ActionSheetOption>
      </ActionSheetGroup>

      <ActionSheetGroup>
        <ActionSheetOption value="archive">Archive</ActionSheetOption>
        <ActionSheetOption value="delete">Delete</ActionSheetOption>
      </ActionSheetGroup>

      <ActionSheetCancel>Cancel</ActionSheetCancel>
    </ActionSheetContent>
  </ActionSheet>
</template>
    

API Reference

ActionSheet

PropTypeDefault
openbooleanfalse
closeOnClickOutsidebooleantrue
showOverlaybooleantrue

ActionSheet forwards other props and emits from Reka UI’s DialogRoot.

ActionSheetStartResult

      
      type ActionSheetStartResult<O> = 
  | { cancelled: false; cancelledReason: null; selectedOption: O }
  | { cancelled: true; cancelledReason: "cancel" | "close"; selectedOption: null }