Skip to content

BbDialog

Presents modal dialogs; also the v-bb-dialog directive.

import { BbDialog } from 'bitboss-ui';

On this page

Props

NameDescription
adaptive Type: booleanDefaultconfig.adaptive (true)

On mobile, renders as a BbOffCanvas drawer instead of a centred dialog; the surface is chosen when the dialog opens and kept until it closes. Unset: the global config.adaptive.

Three-state on purpose: unset falls back to config.adaptive, which ships on, so most projects never write it. The surface is latched when the dialog opens, so a resize mid-session never swaps a modal for a sheet.

See example
defaultData Type: Record<string, any>

What data is when an opening passes none (a create): an object, copied at once, so inputs bind data.x with no guard. An opening's data lays over it ({ ...defaultData, ...row }, one level). reset() restores its keys but keeps an edit's id; a close restores it.

What a create binds: every opening without data gets a fresh copy, an edit lays its row over it (one level). reset() restores the keys it declares and keeps the rest (an edit's id stays); every close restores it. Without it data is undefined on a create.

See example
description Type: string

One short line under the title, muted (the shadcn DialogDescription shape), and the only element aria-describedby points at. Keep it to a sentence; anything longer belongs in the body.

eager Type: booleanDefaultfalse

Keep the body mounted while closed, from before the first opening. Without it the body mounts per opening and unmounts after @hidden. data still clears while closed, so an eager body guards its reads.

Reach for it only when something has to read or measure the body before it has ever been open — a chart, a test, an SSR snapshot. Leaving it off is what keeps a page with many dialogs cheap.

See example
focusTarget Type: string

CSS selector of the element to focus after opening. Defaults to the first tabbable element.

Chooses where focus starts, never whether it is contained: showModal() traps focus and returns it to the trigger either way. Without it the first tabbable element gets focus, which is the × in the header; point it at the field in a form and at the primary action in a decision.

See example
fullscreen Type: boolean | "mobile"

Display the dialog fullscreen. Use 'mobile' to restrict fullscreen to small devices.

Fills the viewport minus the page margin (20px, 14px on mobile) — deliberately not edge to edge. It overrides size rather than combining with it.

See example
hideClose Type: booleanDefaultfalse

Hide the default close (×) button. The button is shown by default; set this to remove it (e.g. for a persistent dialog you don't want dismissible — the × otherwise still closes a persistent dialog).

Inverted from v2's show-close, which defaulted to true. A leftover :show-close="false" type-checks, falls through to $attrs, and the × comes back. Removing it also removes the last one-click way out, so the footer then owes the user a visible Cancel.

See example
id Type: string

Names the dialog for v-bb-dialog="'edit'" and useBbDialogContext, found nearest to the trigger, so two pages may reuse an id. Also the DOM id: one dialog per id, rows pass data.

One dialog per id: it is also the DOM id. Many rows open the same dialog with their own data (v-bb-dialog="{ id, data: row }") rather than rendering one each.

See example
modelValue Type: booleanDefaultfalse

Used by v-model to open / close the dialog. A dialog with no v-model starts closed.

navigation Type: booleanDefaultfalse

Syncs the open state with the URL: opening writes ?<queryKey>=true, closing removes it, a URL with the key opens the dialog on load, and Back closes it (refused whenever Escape is). Template dialogs only; keep a record id in your own query.

Back closes the dialog like Escape (reason 'escape', refused where Escape is). Template dialogs only; a record id stays in your own query.

See example
offCanvasProps Type: Partial<BbOffCanvasProps>

Props for the phone sheet (the BbOffCanvas that adaptive renders on mobile), spread over its defaults. A pt inside it styles only the sheet (passthrough guide § Inside the sheet).

Merged last, so it wins over both the forwarded dialog props and the sheet's own defaults. The drawer's edge prop is side — direction was its v2 name. Spelled canvas-props in v2.

See example
persistent Type: booleanDefaultfalse

Blocks closing by outside click or Escape. The header close (×) STILL closes a persistent dialog, the documented escape hatch: add hideClose to remove it, or use preventClose to block every dismissal. Programmatic close still works.

Blocks the backdrop and Escape only. The × still closes, by design, so persistent alone never makes a dialog unresolvable — pair it with hideClose when the choice must be made explicitly.

See example
preventClose Type: booleanDefaultundefined (follows a saving form inside)

Refuses every user close (backdrop, Escape, the ✕, the slots' close) with the deny nudge; v-model still closes. Unset: locked while a BbForm inside saves (an async @submit). true: always; false: never, not even mid-save. Beside a form: busy || undefined.

Rarely written: unset, the dialog already locks itself while a BbForm inside it saves. true locks it for work that is not a form submit; false keeps it closable even mid-save. The slots' close is refused too, so close with complete(value) or hide() (or v-model) once the work is done.

See example
pt Type: PtMap<BbDialogPtPart, BbDialogPtState>

Passthrough, object form: the same keys as the pt:* attributes without the prefix. See the pt:<part> row.

pt:<part> Type: PtValue

Passthrough, attribute form: one attribute per part, pt:<part>, and per part and state, pt:<part>:<state>. Parts: close, content, description, footer, header, root, sheet, title. States: fullscreen, open.

See example
queryKey Type: stringDefault"dialog"

The query key navigation writes.

Two URL dialogs on one page need their own key. BbOffCanvas defaults to 'offcanvas', so a dialog and a panel never collide.

See example
replace Type: booleanDefaultfalse

With navigation, replaces the history entry instead of pushing one, so Back leaves the page instead of closing the dialog.

Swaps the history entry instead of pushing one, so Back leaves the page rather than closing the dialog.

See example
size Type: Responsive<NonNullable<number | CssSize | keyof BbDialogSizes | undefined>>Default"sm"

Controls the maximum width of the dialog. Accepts named presets from 'xs' to '2xl', a custom CSS width string, or a numeric pixel value — or a per-breakpoint map (e.g. { default: 'sm', lg: 'lg' }) to switch width responsively.

A maximum width; the height always hugs the content. The v3 presets are narrower than v2's { sm: 384, md: 652, lg: 896 } and nothing warns — pin the old numbers with dialogDefaultSizes if a layout assumed them. Only the per-breakpoint map form re-resolves on a resize.

See example
snap Type: BbSnapPoint

The phone sheet's snap stop (Q71.8), one of the snapPoints entries. The desktop dialog ignores it.

snapPoints Type: BbSnapPoint[]

Heights the phone sheet rests at, smallest first: a screen fraction (0.5; 1 = as tall as it gets) or pixels ('240px'). The first is the opening height; v-model:snap reads and sets the stop. Sheet only: the centred dialog ignores it.

Reaches the phone sheet only; the centred modal ignores it.

See example
stack Type: string

Name of this dialog's stack. Dialogs with the same stack replace one another: only the latest opened shows, the rest of the stack hide beneath it. Dialogs in different stacks, or with no stack, never affect each other and stay fully visible.

Members of a dialog stack replace one another. The identically named prop on BbOffCanvas does the opposite — parents step back and peek — which matters because an adaptive dialog forwards this value to its mobile sheet.

See example
title Type: string

Title text announced to assistive technologies. If omitted, no header is rendered (unless a header slot is supplied) and accessibility must be handled manually.

The accessible name, not just a heading: it wires aria-labelledby. Drop it and the header goes too, and naming the dialog becomes your job.

See example
transitionDuration Type: numberDefault250

Controls the duration of the open and close animations.

Times both the animation and when hidden fires. Reopening inside that window keeps the dialog open rather than snapping it shut. Default is 250ms; v2 used 300.

See example

v-model

Values kept in sync through v-model.

NameDescription
modelValue Type: boolean

For a dialog your state opens (a route, a watcher); a button opens one by id with v-bb-dialog. Bound, it wins over the directive and show(). Toggling it back from @hide to veto a close is not the way to keep a dialog open — that is what persistent is for.

See example
snap Type: BbSnapPoint

Events

NameDescription
hidden Type: (): void

Emitted after the close transition completes and the dialog is fully dismissed.

Fires after the close transition, transitionDuration later than hide. The place to tear down expensive content, not to cancel a close.

See example
hide Type: (result: BbDialogResult): void

Emitted as soon as the dialog starts to close (model becomes false), with the opening's result: the same object its promise settles with, { completed: true, value } or { completed: false, reason } (Q73.6).

Fires as the close starts, with the opening's result: { completed: true, value } or { completed: false, reason }. A live preview reverts when completed is false. To tell a save from a cancel, close a save with complete(), not by writing the model: on a v-model dialog too it reports { completed: true }.

See example
show Type: (): void

Emitted as soon as the dialog starts to open (model becomes true).

Fires as the dialog starts to open, before the transition, with the opening's data already set. Nothing to seed here: data is a fresh copy every opening, and defaultData gives a create its blank one.

See example
shown Type: (): void

Emitted once the open transition has run (symmetric with hidden): the dialog is settled, so code that measures or anchors to its content sees the final layout. Not emitted for a dialog closed before it finished opening.

update:modelValue Type: (value: boolean): void

Emitted with the new value whenever v-model changes.

update:snap Type: (value: BbSnapPoint | undefined): void

Emitted with the new value whenever v-model:snap changes.

Slots

The listed properties are the ones exposed to the slot scope.

NameDescription
default Type: BbDialogDefaultSlotProps

Primary body content of the dialog, rendered in the scrollable body area.

The only region that scrolls, and it renders lazily — nothing inside exists until the first open unless you set eager. Its scope carries the opening's data (a copy, so a form binds it directly), close and complete.

See example
description Type: BbDialogDescriptionSlotProps

Replaces the description text under the title. Renders the header even when title and description are unset — a slotted description is a description, the same rule #title follows.

header Type: BbDialogHeaderSlotProps

Replaces the whole header (title and ✕) on both surfaces, so it owns closing: wire your control to close, and titleId onto your title. It renders even without title. Its root is a flex item: give it flex-1 to fill the width.

Replaces the header on both surfaces, so it also replaces the ×: render your own control wired to close, and put titleId on your heading or aria-labelledby points at nothing. The header is a flex row, so give your root flex-1 or it hugs the left edge. This is where a v2 #close goes; a v2 description is the description prop or #description slot.

See example
title Type: BbDialogTitleSlotProps

Replaces the default title text inside the header. Remains linked to the dialog through aria-labelledby. Providing this slot renders the header even when title is unset — a slotted title is a title.

Renamed and removed

Names from v2 and from earlier 3.0 betas that no longer work, and what to write instead.

  • compactremovedfails silently
  • hideHeaderremovedfails silently
  • overlayClassesremovedfails silently
  • panelClassesremovedfails silently
  • showClosehideClosefails silently