Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Overlays

Dialog

Overlays

Dialog React component for Tailwind CSS v4

Context MenuDrawer
Live props
State
defaultOpen
Content
title
description
hideTitle
closeLabel
Appearance
tone
color
size
radius
headerAlign
showClose
Layout
scrollBehavior
Behavior
dismissible
responsive
shortcut
modal
Code
import { Dialog } from "@/components/ui/dialog"

<Dialog />
  1. Home
  2. Components
  3. Overlays
  4. Dialog
Free

Dialog React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React modal dialog for focused tasks: a spring-scaled card on desktop and a bottom sheet on phones, with nesting and scrolling.

Click the trigger and the page dims while the card scales up from 96% on a smooth spring; the icon tile pops in a beat later. Focus moves to the first field, Tab stays inside, and Escape, a click on the dim page or the round × close it, handing focus back to the trigger. A long body scrolls between the pinned header and footer, and a hairline fades in under the header once it scrolls and above the footer while there is more below; with scrollBehavior outside the whole card scrolls with the page instead. A dialog opened from inside another one stacks on top while the first steps back and up, and closing it brings the first forward again. When it is not dismissible, a click outside or Escape only gives the card a small bump: an action has to answer it. A shortcut, when set, toggles it from anywhere. Below 768px it opens as a bottom sheet you can drag down to close, with the actions stacked full width, 48px tall and the primary on top. With reduced motion it only fades.

Dialog is a free React component in the React overlay components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add dialog, together with Button, Drawer, Motion presets, Tones, useControllableState and 4 more and the npm packages @radix-ui/react-dialog, @radix-ui/react-visually-hidden, lucide-react and motion.

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add dialog

Usage

Import Dialog from @/components/ui/dialog.

import { Pencil } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Dialog, DialogClose } from "@/components/ui/dialog"

export function EditListing({ saving, onSave }: { saving: boolean; onSave: () => void }) {
  return (
    <Dialog
      trigger={
        <Button variant="soft" leftIcon={<Pencil />}>
          Edit
        </Button>
      }
      title="Edit listing"
      description="Changes show up for buyers right away."
      icon={<Pencil />}
      closeLabel="Close dialog"
      shortcut="mod+e"
      footer={
        <>
          <DialogClose asChild>
            <Button variant="soft">Cancel</Button>
          </DialogClose>
          <Button loading={saving} loadingText="Saving" onClick={onSave}>
            Save
          </Button>
        </>
      }
    >
      <p className="text-sm text-muted-foreground">Title, price, city and description</p>
    </Dialog>
  )
}

Props

  • defaultOpendefault false
    boolean

    Starts open (uncontrolled). Pass open and onOpenChange instead to control it.

  • open
    boolean

    Controlled open state. The root keeps it across the 768px breakpoint, so resizing swaps the card for the sheet and keeps it open.

  • onOpenChange
    (open: boolean) => void

    Called when it opens or closes: the trigger, Escape, an outside click, a drag on the sheet, a close button, the shortcut.

  • titledefault ""
    ReactNode

    The title, also the dialog's accessible name. Always pass one: without it a visually hidden “Dialog” stands in so screen readers still get a name. The demo shows its sample while this is empty.

  • descriptiondefault ""
    ReactNode

    A quieter line under the title, linked with aria-describedby. Without one the dialog opts out of the description (Radix's aria-describedby={undefined}).

  • hideTitledefault false
    boolean

    Keeps the title for screen readers only, for dialogs whose content speaks for itself (a photo, a video).

  • closeLabeldefault "Close"
    string

    aria-label of the × button, e.g. “Close dialog”. The demo spells it out under the page.

  • trigger
    ReactNode

    The element that opens it, rendered with asChild: a single element that takes a ref, like a Button. It gets aria-keyshortcuts when a shortcut is set. Leave it out and control open yourself.

  • icon
    ReactNode

    An icon in a 40px tile before the title (above it when the header is centered), painted in the tone's soft color. It pops in on a bouncy spring as the card lands.

  • children
    ReactNode

    The body. It scrolls on its own between the pinned header and footer (overscroll contained) and drives the hairlines. Forms inside get the first field focused on open.

  • footer
    ReactNode

    The actions, primary last in the DOM: on the right in a row on desktop, on top when stacked. On the phone sheet they stack full width at 48px whatever size you passed. Wrap Cancel in DialogClose.

  • tonedefault "neutral"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    The icon tile's color: success for a confirmation, danger for a warning about data loss. Needs an icon.

  • colordefault ""
    string

    Any CSS color for the icon tile. Replaces the tone; the icon is mixed toward the text color so it stays readable. Leave empty to use the tone.

  • sizedefault "md"
    "sm" | "md" | "lg" | "xl" | "full"

    Max width 400 / 520 / 720 / 960px, always 16px clear of the screen edges. full fills the screen minus 16px on desktop (a lightbox, a long editor) and becomes a full-height sheet on phones.

  • radiusdefault "lg"
    "none" | "sm" | "md" | "lg" | "full"

    Corners 0 / 12 / 16 / 24px (full stays 24px); the icon tile follows. Dialogs default to lg like every large surface. The phone sheet takes 0 / 16 / 24 / 28px.

  • headerAligndefault "start"
    "start" | "center"

    start reads like a form: the icon beside the title. center reads like an announcement: the icon above a centered title, and the actions centered too.

  • showClosedefault true
    boolean

    The round × in the top corner (ghost, 32px with a 44px hit area on touch). It closes even a dialog that is not dismissible.

  • scrollBehaviordefault "inside"
    "inside" | "outside"

    inside: the card stops at 85% of the screen (720px at most), the header and footer stay and the body scrolls. outside: the card grows with its content and the dimmed page scrolls instead, for long reading. The phone sheet and size full always scroll inside.

  • dismissibledefault true
    boolean

    false: outside clicks, Escape, the shortcut and the sheet drag do not close it; the card gives a small bump instead and the sheet loses its handle. The × and actions (DialogClose, or setting open) still close it, e.g. a consent that must be answered.

  • responsivedefault true
    boolean

    Below 768px it opens as the drawer's bottom sheet: drag to dismiss, safe-area padding, a 92% height for size full. Off, it stays a centered card on phones too.

  • shortcutdefault ""
    string

    A key combo in mod+k notation (⌘ on Apple devices, Ctrl elsewhere) that toggles the dialog from anywhere on the page; the trigger gets aria-keyshortcuts. Empty binds nothing.

  • modaldefault true
    boolean

    false: no backdrop, no focus trap and no scroll lock; the page behind stays usable (Radix's modal), and on desktop a click on the page closes it. scrollBehavior outside needs the backdrop, so it scrolls inside then.

  • onOpenAutoFocus
    (event: Event) => void

    Runs when it opens, before focus moves. Desktop focuses the first focusable element (put the close button last, as the dialog does); the phone sheet focuses itself so no keyboard pops up. Call event.preventDefault() and focus something yourself.

  • onCloseAutoFocus
    (event: Event) => void

    Runs when it closes, before focus returns to the trigger. Prevent it to send focus elsewhere.

  • container
    HTMLElement | null

    Portal target, document.body by default.

  • classNames
    Partial<Record<"overlay" | "content" | "header" | "icon" | "title" | "description" | "body" | "footer" | "close", string>>

    Extra classes for the inner parts, in both modes: on phones they land on the sheet's parts, which carry the same data-slot="dialog-*" names (the backdrop and the handle keep drawer-overlay / drawer-handle).

  • className
    string

    Extra classes for the card (dialog-content), merged last.

Parts

  • DialogRoot

    component

    Compound API: holds the state and picks the mode (a Radix dialog, or the drawer's sheet below 768px). No DOM. Rendered inside another dialog's content it nests: the parent steps back while it is open. Props: { open, defaultOpen, onOpenChange, dismissible, responsive, modal, shortcut, container }.

  • DialogContent

    component

    The portal, the backdrop (dialog-overlay), the card (dialog-content with data-size, data-scroll-behavior, data-nested, data-child-open) and the ×. Needs a DialogTitle inside (visuallyHidden if the design has none); pass aria-describedby={undefined} when there is no description. Props: { size, radius, scrollBehavior, showClose, closeLabel, container, classNames } & Radix Dialog.Content props.

  • DialogHeader

    component

    The icon tile, title and description; keeps clear of the ×. Its bottom hairline fades in once the body scrolls. Without a title (and no children of its own) it renders a hidden “Dialog” title. Props: { title?, description?, hideTitle?, icon?, tone?, color?, align?: "start" | "center" }.

  • DialogTitle

    component

    18px semibold title (17px on the sheet), wired to aria-labelledby. Props: { visuallyHidden?: boolean } & Radix Dialog.Title props.

  • DialogDescription

    component

    A muted 14px description, wired to aria-describedby. Takes Radix Dialog.Description props.

  • DialogIcon

    component

    The 40px tone tile on its own, for custom headers. Props: { tone?, color? } & ComponentProps<"span">.

  • DialogBody

    component

    The scroll region between header and footer. Reports its scroll edges for the hairlines; on the sheet it scrolls first and only then lets the sheet drag. A long text with nothing focusable inside joins the Tab order (with a ring) so the arrow and Page keys scroll it. Takes div props.

  • DialogFooter

    component

    The actions, primary last: a right-aligned row from 640px, stacked with the primary on top below that, and 48px full-width buttons on the phone sheet. Its top hairline shows while the body has more below. Takes div props.

  • DialogTrigger

    component

    Opens the dialog from any element. Takes Radix Dialog.Trigger props (asChild).

  • DialogClose

    component

    Closes the dialog from any element, also one that is not dismissible. Takes Radix Dialog.Close props (asChild).

  • useDialog

    hook

    The nearest dialog's state, e.g. to close it after a form saves, or to lay out differently on the phone sheet. Signature: () => { open: boolean, setOpen: (open: boolean) => void, sheet: boolean }.

Examples

Sizes

sm to xl: 400, 520, 720 and 960px. The same content reflows to the width.

Long content

Inside pins the header and footer, with hairlines that follow the scroll; outside lets the whole card scroll with the page.

Nested

Pick the city from a second dialog: the address form steps back while it is open.

Icon and tone

A success tile, a centered header and a share action, opened after a fake 1s request.

Not dismissible

dismissible={false} and no ×: Esc and outside clicks only bump the card until you choose.

Full size photo preview

size="full" keeps 16px from every edge; a full-height sheet on phones. Arrow keys page through the photos.

Keyboard shortcut

shortcut="mod+e" toggles it from anywhere; the trigger shows the key and gets aria-keyshortcuts.

Dependencies

@radix-ui/react-dialog@radix-ui/react-visually-hiddenlucide-reactmotionbuttondrawermotiontonesuse-controllable-stateuse-media-queryuse-scroll-edgesuse-shortcutui-config

Files

  • components/ui/dialog.tsx

FAQ

Is the React Dialog component free?

Yes. The source is free and needs no account: copy it from the Code tab or install it with the CLI.

How do I install Dialog?

Run npx wingo-ui@latest add dialog. It also adds the Wingo UI items it builds on (Button, Drawer, Motion presets, Tones, useControllableState and 4 more) and the npm packages @radix-ui/react-dialog, @radix-ui/react-visually-hidden, lucide-react and motion. With shadcn, run npx shadcn@latest add https://wingo-ui.com/r/dialog.json.

How does Dialog behave on phones?

Below 768px it opens as the drawer's bottom sheet: drag to dismiss, safe-area padding, a 92% height for size full. Off, it stays a centered card on phones too. The responsive prop controls this and is on by default.

See also

DrawerAlert DialogSheetButtonuseScrollEdgeshook

Used in

Avatar UploadFile managerDeal PipelineSchema BuilderShare DialogShortcuts DialogCookie ConsentApproval FlowMembers tableFile SystemAction SheetVideo Section

and 15 more.

More React overlay components

Family DrawerHover CardPopoverTourTooltipDropdown Menu
All 13 React overlay components

Articles using this component

  • OpenCode MCP Setup: opencode.json for v1 and v2

    Building UI with AI agents · Nov 6, 2026 · 11 min read

  • Windsurf MCP Setup in 2026: New Config Path, Rules, Fixes

    Building UI with AI agents · Nov 6, 2026 · 10 min read

  • Copilot shadcn Setup: MCP Server and Instructions in VS Code

    Building UI with AI agents · Nov 5, 2026 · 10 min read

  • Chakra UI vs shadcn in 2026: Style Props or Tailwind?

    Comparisons and alternatives · Oct 16, 2026 · 10 min read

PreviousContext MenuNext Drawer

My listings

Updated yesterday, 6:40 PM

Pegas city bike

$420

Brooklyn · 214 views

Pegas City 1, medium frame, 3-speed hub. Ridden for two summers, serviced in May. Comes with a basket, a lock and a battery light.

Click Edit to open it. Press Esc, click outside or use × (“Close”) to close it. A long form scrolls between the pinned header and footer. Below 768px it opens as a bottom sheet.