Skip to content

BottomSheet ​

Konsta-based sheet that slides up from the bottom of the viewport for short, mobile-first confirmations (archive, filter, pick). It teleports to <body>, so no ancestor transform/filter/overflow can trap it.

vue
<script setup lang="ts">
import RyderBottomSheet from "@opeolluwa/ryder/components/feedback/BottomSheet.vue"
</script>

<template>
  <RyderBottomSheet v-model:open="open" title="Archive order" @submit="open = false">
    <p>Sheet body.</p>
  </RyderBottomSheet>
</template>

Examples ​

vue
<script setup lang="ts">
import { ref } from "vue";
import BottomSheet from "../../../src/components/feedback/BottomSheet.vue";

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

<template>
  <div class="space-y-3">
    <button
      type="button"
      class="rounded-lg bg-primary-600 px-4 py-2 text-sm font-medium text-white hover:bg-primary-700"
      @click="open = true"
    >
      Open bottom sheet
    </button>

    <BottomSheet
      v-model:open="open"
      title="Archive orders"
      description="Archived orders leave the active list."
      submit-label="Archive"
      @submit="open = false"
      @cancel="open = false"
    >
      <p class="text-sm text-gray-500 dark:text-white/50">
        Body content scrolls between the fixed header and the footer row.
      </p>

      <p class="pt-2 text-sm text-gray-500 dark:text-white/50">
        Tapping the backdrop (or the X) emits
        <code class="font-mono">cancel</code> and closes the sheet on its own.
      </p>
    </BottomSheet>
  </div>
</template>

Props ​

PropTypeDefaultDescription
titlestring—Required; the sheet's heading, rendered next to the close button.
descriptionstring | undefinedundefinedSubtext under the heading (hidden when empty).
loadingboolean | undefinedundefinedFalsy by default; truthy shows a spinner on submit and disables the close and footer buttons.
submitDisabledboolean | undefinedundefinedFalsy by default; truthy disables only the default submit button (e.g. until a form is valid).
dismissiblebooleantrueWhen false, the close button is hidden and backdrop taps are ignored.
submitLabelstring | undefinedundefinedFalls back to "Submit" in the template when unset.
cancelLabelstring | undefinedundefinedFalls back to "Cancel" in the template when unset.

Emits ​

EventPayloadWhen
submit—The built-in submit button was tapped. The sheet stays open — the app closes it. A custom footer slot never triggers it.
cancel—Close button, backdrop tap (when dismissible), or the built-in cancel button was tapped. The sheet also closes itself.
leave—open flipped to false; the close transition starts.
after:leave—400 ms after leave — mirrors konsta's duration-400, so Teleported content can unmount at the right time.

Slots ​

SlotPropsContent
default—Scrollable body between the header and the footer (max-h-[85dvh]).
footer—Replaces the built-in cancel/submit row entirely.

Notes ​

  • Konsta dependency (kSheet) plus Nuxt UI's UButton for the close button; the konsta theme CSS (konsta/vue/theme.css) must be loaded in the app.
  • The built-in submit button emits submit and does not close the sheet — the app owns closing. Cancel does both.
  • Body area handles the safe-area: padding-bottom: env(safe-area-inset-bottom).
  • The 400 ms mirror of konsta's transition exists so after:leave lands at the right moment — the same contract Nuxt UI overlays use.