Dialog
Usage
import { Button } from '@jamie/ui/button'
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger
} from '@jamie/ui/dialog'
export function DialogDemo() {
return (
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>Share Summary</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Share meeting summary</DialogTitle>
<DialogDescription>
This will share the summary of “Q3 Revenue Review” with all meeting
participants via email.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose render={<Button variant="outline" />}>Cancel</DialogClose>
<Button>Share</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}With Form
Embed form fields inside a dialog for inline data entry.
'use client'
import { Button } from '@jamie/ui/button'
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger
} from '@jamie/ui/dialog'
import { Input } from '@jamie/ui/input'
import { Label } from '@jamie/ui/label'
import { useState } from 'react'
export function DialogWithFormDemo() {
const [open, setOpen] = useState(false)
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger render={<Button variant="outline" />}>Create Agenda</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Create meeting agenda</DialogTitle>
<DialogDescription>
Set up the agenda for your upcoming meeting. Participants will be notified.
</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid gap-2">
<Label htmlFor="title">Meeting Title</Label>
<Input id="title" placeholder="Q4 Budget Planning" />
</div>
<div className="grid gap-2">
<Label htmlFor="topics">Topics</Label>
<Input id="topics" placeholder="Revenue targets, headcount, vendor costs" />
</div>
</div>
<DialogFooter>
<DialogClose render={<Button variant="outline" />}>Cancel</DialogClose>
<Button onClick={() => setOpen(false)}>Create</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}Custom Close Button
Replace the default close control with your own buttons in the footer.
import { Button } from '@jamie/ui/button'
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger
} from '@jamie/ui/dialog'
export function DialogCloseButtonDemo() {
return (
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>Delete Recording</DialogTrigger>
<DialogContent showCloseButton={false}>
<DialogHeader>
<DialogTitle>Delete recording?</DialogTitle>
<DialogDescription>
This will permanently remove the recording and its transcript. This action cannot be
undone.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose render={<Button variant="outline" />}>Keep Recording</DialogClose>
<Button variant="destructive">Delete</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}No Close Button
Use showCloseButton={false} to hide the close button.
import { Button } from '@jamie/ui/button'
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger
} from '@jamie/ui/dialog'
export function DialogNoCloseButtonDemo() {
return (
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>Export Notes</DialogTrigger>
<DialogContent showCloseButton={false}>
<DialogHeader>
<DialogTitle>Export meeting notes</DialogTitle>
<DialogDescription>
Choose a format to export the notes from “Weekly Standup”.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose render={<Button variant="outline" />}>Cancel</DialogClose>
<Button>Export as PDF</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}Sticky Footer
Keep actions visible while the content scrolls. Use the showCloseButton prop on DialogFooter.
import { Button } from '@jamie/ui/button'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger
} from '@jamie/ui/dialog'
const actionItems = [
{ title: 'Update Q4 revenue projections', assignee: 'Sarah', due: 'Feb 20' },
{ title: 'Schedule vendor review meeting', assignee: 'Mike', due: 'Feb 22' },
{ title: 'Prepare headcount proposal', assignee: 'Lisa', due: 'Feb 25' },
{ title: 'Review marketing budget allocation', assignee: 'James', due: 'Feb 28' },
{ title: 'Draft customer success playbook', assignee: 'Sarah', due: 'Mar 1' },
{ title: 'Analyze NPS survey by segment', assignee: 'James', due: 'Mar 3' },
{ title: 'Set up retention task force kickoff', assignee: 'Mike', due: 'Mar 5' },
{ title: 'Finalize Q4 content calendar', assignee: 'Lisa', due: 'Mar 7' },
{ title: 'Present product launch marketing plan', assignee: 'James', due: 'Mar 10' },
{ title: 'Coordinate cross-team OKR alignment', assignee: 'Sarah', due: 'Mar 12' }
]
export function DialogStickyFooterDemo() {
return (
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>Review Action Items</DialogTrigger>
<DialogContent className="max-h-[min(80vh,28rem)] flex flex-col">
<DialogHeader>
<DialogTitle>Action items</DialogTitle>
<DialogDescription>
Review the action items captured during the meeting.
</DialogDescription>
</DialogHeader>
<div className="-mx-4 flex-1 overflow-y-auto px-4">
<div className="grid gap-3 text-sm">
{actionItems.map((item) => (
<div key={item.title} className="rounded-lg border p-3">
<p className="font-medium">{item.title}</p>
<p className="text-muted-foreground mt-1">
Assigned to {item.assignee} · Due {item.due}
</p>
</div>
))}
</div>
</div>
<DialogFooter showCloseButton>
<Button>Send Reminders</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}Scrollable Content
Long content can scroll while the header stays in view.
import { Button } from '@jamie/ui/button'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger
} from '@jamie/ui/dialog'
const transcript = [
{
name: 'Sarah Chen',
text: 'Let\u2019s start with the revenue numbers for Q3. Overall, we\u2019re tracking about 12% above our initial projections, which is great news.'
},
{
name: 'Mike Rodriguez',
text: 'That\u2019s largely driven by the enterprise segment. We closed three major deals that weren\u2019t in the original forecast.'
},
{
name: 'Lisa Park',
text: 'On the SMB side, we\u2019re seeing higher churn than expected. I think we need to revisit our onboarding process.'
},
{
name: 'Sarah Chen',
text: 'Agreed. Let\u2019s put together a task force to look at that. Mike, can you lead the effort on improving retention?'
},
{
name: 'Mike Rodriguez',
text: 'Sure. I\u2019ll set up a kickoff meeting next week and loop in the customer success team. We should also look at the data from our recent NPS survey.'
},
{
name: 'James Wilson',
text: 'I can pull the NPS data and segment it by account size. That should help us identify where the pain points are.'
},
{
name: 'Lisa Park',
text: 'One more thing \u2014 the marketing budget for Q4. We need to decide if we\u2019re increasing spend on paid channels or shifting more toward content.'
},
{
name: 'Sarah Chen',
text: 'Good point. Content has been performing well organically, but paid is what drives the short-term pipeline. We probably need a mix.'
},
{
name: 'Mike Rodriguez',
text: 'I\u2019d lean toward keeping paid at current levels and investing the incremental budget into content and SEO. The ROI on content compounds over time.'
},
{
name: 'James Wilson',
text: 'I agree with Mike. Our blog traffic is up 40% quarter-over-quarter, and we\u2019re seeing strong conversion from organic search.'
},
{
name: 'Lisa Park',
text: 'We should also think about the product launch in November. That\u2019s going to need dedicated marketing spend regardless of the channel mix.'
},
{
name: 'Sarah Chen',
text: 'Right. Let\u2019s allocate a separate budget for the launch campaign. James, can you put together a proposal by end of next week?'
},
{
name: 'James Wilson',
text: 'Absolutely. I\u2019ll coordinate with the product team to align messaging and timing.'
},
{
name: 'Sarah Chen',
text: 'Let\u2019s table that for now and revisit once we have the full Q3 report. I want to make sure we\u2019re making data-driven decisions there.'
}
]
export function DialogScrollableContentDemo() {
return (
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>View Transcript</DialogTrigger>
<DialogContent className="max-h-[min(80vh,28rem)] flex flex-col">
<DialogHeader>
<DialogTitle>Meeting transcript</DialogTitle>
<DialogDescription>
Full transcript for “Q3 Revenue Review”.
</DialogDescription>
</DialogHeader>
<div className="-mx-4 flex-1 overflow-y-auto px-4">
<div className="grid gap-4 text-sm">
{transcript.map((entry) => (
<div key={entry.name}>
<p className="font-medium">{entry.name}</p>
<p className="text-muted-foreground">{entry.text}</p>
</div>
))}
</div>
</div>
<DialogFooter showCloseButton />
</DialogContent>
</Dialog>
)
}