---
title: Modal
description: A lightweight dialog for short blocking tasks.
category: Feedback
status: beta
since: 0.3.4
tags: [modal, dialog, overlay]
syncStatus: reviewed
verified: true
---

## Usage

### Basic
`v-model` controls visibility, and the `footer` slot holds the actions.
::::TuffDemoWrapper{demo="ModalBasicDemo" code-lang="vue"}
---
code: |
  <template>
    <TxButton variant="primary" @click="open = true">Open Modal</TxButton>
    <TxButton variant="ghost" @click="fullscreen = true">Open fullscreen preview</TxButton>

    <TxModal v-model="open" title="Confirm sync settings" width="min(92vw, 520px)">
      <p>Use a modal for short confirmations or compact forms.</p>
      <template #footer>
        <TxButton variant="ghost" @click="open = false">Cancel</TxButton>
        <TxButton variant="primary" @click="open = false">Confirm sync</TxButton>
      </template>
    </TxModal>

    <TxModal v-model="fullscreen" fullscreen title="Release notes">
      <p>Long content scrolls inside the panel; the bars stay put.</p>
      <template #footer>
        <TxButton variant="ghost" @click="fullscreen = false">Close preview</TxButton>
      </template>
    </TxModal>
  </template>
---
::::

### Fullscreen Panel
`fullscreen` fills the visible viewport: the body scrolls, the header and footer stay fixed, and the footer clears the bottom safe area.

```vue
<TxModal v-model="previewOpen" fullscreen :title="current?.name">
  <img :src="current.url" alt="">
  <template #footer>
    <TxButton variant="ghost" @click="previewOpen = false">Close</TxButton>
  </template>
</TxModal>
```

### Best Practices

- Limit a modal to a confirmation, one-step input, or short decision; use a drawer or page for navigation, filtering, or long forms.
- With a custom `header`, keep a visible title and leave `title` empty, or `aria-labelledby` points at a missing element.
- Put destructive or final actions in the `footer`, and keep secondary actions visually quieter than the primary one.
- Use `fullscreen` only when the content owns the screen (image or diagram previews), never for a short confirmation.
- Don't keep long-running async state only inside the modal content; if closing cancels work, model the cancellation in the parent.

## API Reference

### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `modelValue` | `boolean` | required | Whether the dialog is shown; used by `v-model`. |
| `title` | `string` | `''` | Default header title; when set, it is linked through `aria-labelledby`. |
| `width` | `string` | `'480px'` | Panel width; prefer responsive values such as `min(92vw, 520px)`. Ignored with `fullscreen`. |
| `fullscreen` | `boolean` | `false` | Fills the visible viewport and drops the panel radius and shadow. |

### Events

| Event | Payload | Description |
|------|---------|-------------|
| `update:modelValue` | `(value: boolean)` | Fires when the component requests a visibility change. |
| `close` | `()` | Fires after a backdrop click, Escape, or the close button. |

### Slots

| Slot | Props | Description |
|------|-------|-------------|
| `default` | - | Dialog body. |
| `header` | - | Replaces the title area; the built-in close button stays. |
| `footer` | - | Footer actions; not rendered when omitted. |

## Overview

- The overlay teleports to `body`, takes a fresh layer from the shared z-index manager on open, and is removed with `v-if` on close.
- The overlay is `role="dialog"` with `aria-modal="true"`; it takes focus on open and returns it to the previous element on close or unmount.
- Tab and Shift+Tab cycle inside the topmost modal; even with focus on `body`, a drawer underneath doesn't react to Escape.
- A backdrop click, Escape, and the close button emit `update:modelValue(false)`, then `close`.
- `fullscreen` changes only the layout, not the dialog semantics or focus handling; it leaves no backdrop to click, so keep a visible close control in the header or footer.
- `TModal` forwards props, attrs, events, and the `default` / `header` / `footer` slots to `TxModal`.

## Technologies

- The fullscreen panel uses a `100dvh` height where supported, and its footer padding includes `safe-area-inset-bottom`.
- Source: `packages/tuffex/packages/components/src/modal/` (`TxModal.vue` and the `TModal.vue` wrapper).

<TuffDocSourceLink />
