shadcn-remix
GitHub

Dialog

A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.

import { css } from 'remix/component'

import { Button, buttonVariants } from '#app/ui/button.tsx'
import {
  Dialog,
  DialogClose,
import { css } from 'remix/component'

import { Button, buttonVariants } from '#app/ui/button.tsx'
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from '#app/ui/dialog.tsx'
import { Input } from '#app/ui/input.tsx'
import { Label } from '#app/ui/label.tsx'

const grid4 = css({ display: 'grid', gap: '1rem' })
const grid3 = css({ display: 'grid', gap: '0.75rem' })

export function DialogDemo() {
  return () => (
    <Dialog>
      <form>
        <DialogTrigger mix={buttonVariants({ variant: 'outline' })}>Open Dialog</DialogTrigger>
        <DialogContent mix={css({ '@media (min-width: 40rem)': { maxWidth: '425px' } })}>
          <DialogHeader>
            <DialogTitle>Edit profile</DialogTitle>
            <DialogDescription>
              Make changes to your profile here. Click save when you&apos;re done.
            </DialogDescription>
          </DialogHeader>
          <div mix={grid4}>
            <div mix={grid3}>
              <Label htmlFor="name-1">Name</Label>
              <Input id="name-1" name="name" defaultValue="Pedro Duarte" />
            </div>
            <div mix={grid3}>
              <Label htmlFor="username-1">Username</Label>
              <Input id="username-1" name="username" defaultValue="@peduarte" />
            </div>
          </div>
          <DialogFooter>
            <DialogClose mix={buttonVariants({ variant: 'outline' })}>Cancel</DialogClose>
            <Button type="submit">Save changes</Button>
          </DialogFooter>
        </DialogContent>
      </form>
    </Dialog>
  )
}

Installation

$ pnpm dlx shadcn@latest add https://www.shadcn-remix.com/r/dialog.json

Usage

import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from '#app/ui/dialog.tsx'
<Dialog>
  <DialogTrigger>Open</DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Are you absolutely sure?</DialogTitle>
      <DialogDescription>
        This action cannot be undone. This will permanently delete your account
        and remove your data from our servers.
      </DialogDescription>
    </DialogHeader>
  </DialogContent>
</Dialog>

Examples

Custom Close Button

Replace the default close control with your own button.

import { css } from 'remix/component'

import { buttonVariants } from '#app/ui/button.tsx'
import {
  Dialog,
  DialogClose,
import { css } from 'remix/component'

import { buttonVariants } from '#app/ui/button.tsx'
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from '#app/ui/dialog.tsx'
import { Input } from '#app/ui/input.tsx'
import { Label } from '#app/ui/label.tsx'

const srOnly = css({
  position: 'absolute',
  width: '1px',
  height: '1px',
  padding: 0,
  margin: '-1px',
  overflow: 'hidden',
  clip: 'rect(0, 0, 0, 0)',
  whiteSpace: 'nowrap',
  borderWidth: 0,
})

export function DialogCloseButton() {
  return () => (
    <Dialog>
      <DialogTrigger mix={buttonVariants({ variant: 'outline' })}>Share</DialogTrigger>
      <DialogContent mix={css({ '@media (min-width: 40rem)': { maxWidth: '28rem' } })}>
        <DialogHeader>
          <DialogTitle>Share link</DialogTitle>
          <DialogDescription>Anyone who has this link will be able to view this.</DialogDescription>
        </DialogHeader>
        <div mix={css({ display: 'flex', alignItems: 'center', gap: '0.5rem' })}>
          <div mix={css({ display: 'grid', flex: '1 1 0%', gap: '0.5rem' })}>
            <Label htmlFor="link" mix={srOnly}>
              Link
            </Label>
            <Input id="link" defaultValue="https://ui.shadcn.com/docs/installation" readOnly />
          </div>
        </div>
        <DialogFooter mix={css({ '@media (min-width: 40rem)': { justifyContent: 'flex-start' } })}>
          <DialogClose type="button" mix={buttonVariants({ variant: 'secondary' })}>
            Close
          </DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

API Reference

Dialog

PropTypeDefault
openboolean-
defaultOpenboolean-
onOpenChange((open: boolean) => void)-

DialogTrigger

PropTypeDefault
mixMixValue-

DialogPortal

Radix portals the overlay and content to document.body. Here they render inside a native modal <dialog> in the top layer instead: it escapes overflow/stacking contexts, traps focus, makes the rest of the page inert, and closes on Escape. The <dialog> stays open until the exit animations of its overlay and content have finished.

PropTypeDefault
mixMixValue-

DialogClose

PropTypeDefault
mixMixValue-

DialogOverlay

PropTypeDefault
mixMixValue-

DialogContent

PropTypeDefault
showCloseButtonbooleantrue
mixMixValue-

DialogHeader

PropTypeDefault
mixMixValue-

DialogFooter

PropTypeDefault
showCloseButtonbooleanfalse
mixMixValue-

DialogTitle

PropTypeDefault
mixMixValue-

DialogDescription

PropTypeDefault
mixMixValue-