Components / Overlays

Dialog

A native top-layer modal with a stationary header and footer and a scrollable body.

Preview

TEAM PROFILE

Your public details

Visible to team
AM

Ana Martins

Product designer

Keep your details up to date

CAMPAIGN WORKSPACE

Aurora launch

In progress

Bring the launch plan, assets, and tasks together in one focused view.

2 of 4 tasks ready

Install

Run in your project
Package manager
npx @getweini/cli@latest add dialog

Use cases

Edit an object

Keep a longer form in a scrolling body with stationary title and actions.

Inspect details

Open a modal view when the user needs to focus on one record.

Usage example

Vue
<script setup lang="ts">
import { ref, watch } from 'vue'
import {
  Button,
  Checkbox,
  Dialog,
  DialogBody,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
  Field,
  FieldLabel,
  Input,
} from './components'

const editOpen = ref(false)
const workspaceOpen = ref(false)
const name = ref('Ana Martins')
const role = ref('Product designer')
const draftName = ref(name.value)
const draftRole = ref(role.value)
const tasks = ref([
  { id: 'copy', label: 'Review final copy', done: true },
  { id: 'assets', label: 'Approve campaign assets', done: false },
])
const draftTasks = ref(tasks.value.map((task) => ({ ...task })))

watch(editOpen, (open) => {
  if (!open) return
  draftName.value = name.value
  draftRole.value = role.value
})
watch(workspaceOpen, (open) => {
  if (open) draftTasks.value = tasks.value.map((task) => ({ ...task }))
})

function saveProfile() {
  name.value = draftName.value.trim()
  role.value = draftRole.value.trim()
  editOpen.value = false
}

function saveWorkspace() {
  tasks.value = draftTasks.value.map((task) => ({ ...task }))
  workspaceOpen.value = false
}
</script>

<template>
  <Dialog v-model:open="editOpen">
    <DialogTrigger>Edit profile</DialogTrigger>
    <DialogContent v-if="editOpen" size="sm" persistent>
      <DialogHeader>
        <DialogTitle>Edit profile</DialogTitle>
        <DialogDescription>Update how your profile appears to teammates.</DialogDescription>
        <template #actions><DialogClose aria-label="Close dialog"</DialogClose></template>
      </DialogHeader>
      <DialogBody>
        <form id="profile-form" class="grid gap-4" @submit.prevent="saveProfile">
          <Field required><FieldLabel>Display name</FieldLabel><Input v-model="draftName" /></Field>
          <Field required><FieldLabel>Role</FieldLabel><Input v-model="draftRole" /></Field>
        </form>
      </DialogBody>
      <DialogFooter>
        <DialogClose>Cancel</DialogClose>
        <Button type="submit" form="profile-form">Save changes</Button>
      </DialogFooter>
    </DialogContent>
  </Dialog>

  <Dialog v-model:open="workspaceOpen">
    <DialogTrigger>Open workspace</DialogTrigger>
    <DialogContent v-if="workspaceOpen" fullscreen>
      <DialogHeader>
        <DialogTitle>Campaign workspace</DialogTitle>
        <DialogDescription>Review the launch plan and save your progress.</DialogDescription>
        <template #actions>
          <DialogClose aria-label="Close fullscreen dialog"</DialogClose>
        </template>
      </DialogHeader>
      <DialogBody>
        <section class="mx-auto max-w-3xl space-y-4 py-6">
          <h2 class="text-xl font-semibold">Aurora launch</h2>
          <p>Bring the launch plan, assets, and tasks together in one focused view.</p>
          <div v-for="task in draftTasks" :key="task.id">
            <Checkbox v-model="task.done">{{ task.label }}</Checkbox>
          </div>
        </section>
      </DialogBody>
      <DialogFooter>
        <DialogClose>Close workspace</DialogClose>
        <Button @click="saveWorkspace">Save progress</Button>
      </DialogFooter>
    </DialogContent>
  </Dialog>
</template>

More examples

Edit profile

The native dialog manages modality and restores trigger focus.

Edit profile
<script setup lang="ts">
import Dialog from './components/ui/dialog/Dialog.vue'
import DialogTrigger from './components/ui/dialog/DialogTrigger.vue'
import DialogContent from './components/ui/dialog/DialogContent.vue'
import DialogHeader from './components/ui/dialog/DialogHeader.vue'
import DialogTitle from './components/ui/dialog/DialogTitle.vue'
import DialogDescription from './components/ui/dialog/DialogDescription.vue'
import DialogBody from './components/ui/dialog/DialogBody.vue'
import DialogFooter from './components/ui/dialog/DialogFooter.vue'
import DialogClose from './components/ui/dialog/DialogClose.vue'
</script>
<template>
  <Dialog>
    <DialogTrigger>Edit profile</DialogTrigger>
    <DialogContent size="lg">
      <DialogHeader><DialogTitle>Edit profile</DialogTitle><DialogDescription>Update your public details.</DialogDescription></DialogHeader>
      <DialogBody><label>Name <input name="name" /></label></DialogBody>
      <DialogFooter><DialogClose>Done</DialogClose></DialogFooter>
    </DialogContent>
  </Dialog>
</template>

Persistent review

Persistent content stays open on backdrop clicks but still responds to Escape.

Persistent review
<script setup lang="ts">
import Dialog from './components/ui/dialog/Dialog.vue'
import DialogTrigger from './components/ui/dialog/DialogTrigger.vue'
import DialogContent from './components/ui/dialog/DialogContent.vue'
import DialogHeader from './components/ui/dialog/DialogHeader.vue'
import DialogTitle from './components/ui/dialog/DialogTitle.vue'
import DialogBody from './components/ui/dialog/DialogBody.vue'
import DialogFooter from './components/ui/dialog/DialogFooter.vue'
import DialogClose from './components/ui/dialog/DialogClose.vue'
</script>
<template>
  <Dialog>
    <DialogTrigger>Review changes</DialogTrigger>
    <DialogContent persistent>
      <DialogHeader><DialogTitle>Review changes</DialogTitle></DialogHeader>
      <DialogBody><p>Check these changes before you continue.</p></DialogBody>
      <DialogFooter><DialogClose>Close review</DialogClose></DialogFooter>
    </DialogContent>
  </Dialog>
</template>

Key API

The most useful props, models, slots, events, and methods for this component.

Key API for Dialog
NameTypeDefault or requirement
v-model:open / defaultOpenbooleanfalse
DialogContent fullscreenbooleanfalse
DialogContent sizesm | md | lg | xlmd
DialogContent dismissOnBackdropbooleantrue
DialogContent persistentbooleanfalse

Use DialogHeader’s #actions slot for an optional close control. Set persistent on DialogContent when backdrop clicks should leave the form open; Escape and close buttons still dismiss it. Fullscreen keeps the header and footer visible while DialogBody scrolls.

Implementation notes

  • Place substantial content inside DialogBody; it is the independently scrolling row.
  • Persistent takes precedence over dismissOnBackdrop, but Escape and explicit close controls still work.

Accessibility and localization

  • Provide DialogTitle or an explicit accessible name.
  • Use data-dialog-initial-focus on the control that should receive initial focus when the default order is unsuitable.
Weini UIDesign system · Vue component library · Free and public