Side Dialog
A lightweight dialog that opens from the sides of the screen, perfect for notifications, user menus, and contextual actions.
npx shadcn@latest add https://shuip.plvo.dev/r/side-dialog.json
pnpm dlx shadcn@latest add https://shuip.plvo.dev/r/side-dialog.json
bun x shadcn@latest add https://shuip.plvo.dev/r/side-dialog.json
'use client';import { XIcon } from 'lucide-react';import * as React from 'react';import { createPortal } from 'react-dom';import { cn } from '@/lib/utils';type SideDialogPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'left' | 'right';type SideDialogSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';export interface SideDialogProps {open?: boolean;onOpenChange?: (open: boolean) => void;position?: SideDialogPosition;size?: SideDialogSize;children?: React.ReactNode;}export interface SideDialogTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {asChild?: boolean;children: React.ReactNode;}export interface SideDialogContentProps extends React.HTMLAttributes<HTMLDivElement> {showCloseButton?: boolean;children: React.ReactNode;}export interface SideDialogHeaderProps extends React.HTMLAttributes<HTMLDivElement> {children: React.ReactNode;}export interface SideDialogTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {children: React.ReactNode;}export interface SideDialogDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {children: React.ReactNode;}export interface SideDialogFooterProps extends React.HTMLAttributes<HTMLDivElement> {children: React.ReactNode;}export interface SideDialogCloseProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {asChild?: boolean;children?: React.ReactNode;}// Context for managing dialog stateconst SideDialogContext = React.createContext<{open: boolean;onOpenChange: (open: boolean) => void;position: SideDialogPosition;size: SideDialogSize;}>({open: false,onOpenChange: () => {},position: 'bottom-right',size: 'sm',});export function SideDialog({open: controlledOpen,onOpenChange: controlledOnOpenChange,position = 'bottom-right',size = 'sm',children,}: SideDialogProps) {// Internal state for uncontrolled modeconst [internalOpen, setInternalOpen] = React.useState(false);// Use controlled props if provided, otherwise use internal stateconst open = controlledOpen !== undefined ? controlledOpen : internalOpen;const onOpenChange = controlledOnOpenChange !== undefined ? controlledOnOpenChange : setInternalOpen;const contextValue = React.useMemo(() => ({open,onOpenChange,position,size,}),[open, onOpenChange, position, size],);return <SideDialogContext.Provider value={contextValue}>{children}</SideDialogContext.Provider>;}export function SideDialogTrigger({ asChild, children, onClick, ...props }: SideDialogTriggerProps) {const { onOpenChange } = React.useContext(SideDialogContext);const handleClick = React.useCallback((e: React.MouseEvent<HTMLButtonElement>) => {onClick?.(e);onOpenChange(true);},[onClick, onOpenChange],);if (asChild) {return React.cloneElement(children as React.ReactElement, {onClick: handleClick,...(props as React.ButtonHTMLAttributes<HTMLButtonElement>),});}return (<button data-slot='side-dialog-trigger' onClick={handleClick} {...props}>{children}</button>);}export function SideDialogContent({ showCloseButton = true, className, children, ...props }: SideDialogContentProps) {const { open, onOpenChange, position, size } = React.useContext(SideDialogContext);const [mounted, setMounted] = React.useState(false);React.useEffect(() => {setMounted(true);}, []);React.useEffect(() => {const handleEscape = (e: KeyboardEvent) => {if (e.key === 'Escape' && open) {onOpenChange(false);}};if (open) {document.addEventListener('keydown', handleEscape);document.body.style.overflow = 'hidden';}return () => {document.removeEventListener('keydown', handleEscape);document.body.style.overflow = '';};}, [open, onOpenChange]);const handleOverlayClick = React.useCallback((e: React.MouseEvent) => {if (e.target === e.currentTarget) {onOpenChange(false);}},[onOpenChange],);const getSizeClasses = (size: SideDialogSize) => {const predefinedSizes = {xs: 'w-full max-w-[min(20rem,calc(100vw-2rem))]',sm: 'w-full max-w-[min(24rem,calc(100vw-2rem))]',md: 'w-full max-w-[min(28rem,calc(100vw-2rem))]',lg: 'w-full max-w-[min(32rem,calc(100vw-2rem))]',xl: 'w-full max-w-[min(36rem,calc(100vw-2rem))]','2xl': 'w-full max-w-[min(42rem,calc(100vw-2rem))]',};return predefinedSizes[size as keyof typeof predefinedSizes] || 'w-full max-w-[min(24rem,calc(100vw-2rem))]';};const getPositionClasses = (pos: SideDialogPosition, size: SideDialogSize) => {const baseClasses = 'fixed z-[100] bg-background border rounded-lg shadow-lg';const sizeClasses = getSizeClasses(size);switch (pos) {case 'top-left':return cn(baseClasses,sizeClasses,'top-4 left-4','data-[state=open]:animate-in data-[state=closed]:animate-out','data-[state=closed]:slide-out-to-top-52 data-[state=closed]:slide-out-to-left-52','data-[state=open]:slide-in-from-top-52 data-[state=open]:slide-in-from-left-52',);case 'top-right':return cn(baseClasses,sizeClasses,'top-4 right-4','data-[state=open]:animate-in data-[state=closed]:animate-out','data-[state=closed]:slide-out-to-top-52 data-[state=closed]:slide-out-to-right-52','data-[state=open]:slide-in-from-top-52 data-[state=open]:slide-in-from-right-52',);case 'bottom-left':return cn(baseClasses,sizeClasses,'bottom-4 left-4','data-[state=open]:animate-in data-[state=closed]:animate-out','data-[state=closed]:slide-out-to-bottom-52 data-[state=closed]:slide-out-to-left-52','data-[state=open]:slide-in-from-bottom-52 data-[state=open]:slide-in-from-left-52',);case 'bottom-right':return cn(baseClasses,sizeClasses,'bottom-4 right-4','data-[state=open]:animate-in data-[state=closed]:animate-out','data-[state=closed]:slide-out-to-bottom-52 data-[state=closed]:slide-out-to-right-52','data-[state=open]:slide-in-from-bottom-52 data-[state=open]:slide-in-from-right-52',);case 'left':return cn(baseClasses,sizeClasses,'left-4 top-[50%] translate-y-[-50%]','data-[state=open]:animate-in data-[state=closed]:animate-out','data-[state=closed]:slide-out-to-left-52','data-[state=open]:slide-in-from-left-52',);case 'right':return cn(baseClasses,sizeClasses,'right-4 top-[50%] translate-y-[-50%]','data-[state=open]:animate-in data-[state=closed]:animate-out','data-[state=closed]:slide-out-to-right-52','data-[state=open]:slide-in-from-right-52',);default:return cn(baseClasses, sizeClasses);}};if (!mounted) return null;if (!open) return null;return createPortal(<divdata-slot='side-dialog-overlay'className='fixed inset-0 z-40 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0'data-state={open ? 'open' : 'closed'}onClick={handleOverlayClick}><divdata-slot='side-dialog-content'data-state={open ? 'open' : 'closed'}className={cn(getPositionClasses(position, size), 'p-6 duration-200', className)}{...props}>{children}{showCloseButton && (<SideDialogClose className='absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden'><XIcon className='size-4' /><span className='sr-only'>Close</span></SideDialogClose>)}</div></div>,document.body,);}export function SideDialogHeader({ className, children, ...props }: SideDialogHeaderProps) {return (<div data-slot='side-dialog-header' className={cn('flex flex-col gap-2 mb-4', className)} {...props}>{children}</div>);}export function SideDialogTitle({ className, children, ...props }: SideDialogTitleProps) {return (<h2 data-slot='side-dialog-title' className={cn('text-lg font-semibold leading-none', className)} {...props}>{children}</h2>);}export function SideDialogDescription({ className, children, ...props }: SideDialogDescriptionProps) {return (<p data-slot='side-dialog-description' className={cn('text-muted-foreground text-sm', className)} {...props}>{children}</p>);}export function SideDialogFooter({ className, children, ...props }: SideDialogFooterProps) {return (<divdata-slot='side-dialog-footer'className={cn('flex flex-col-reverse gap-2 mt-4 sm:flex-row sm:justify-end', className)}{...props}>{children}</div>);}export function SideDialogClose({ asChild, children, onClick, ...props }: SideDialogCloseProps) {const { onOpenChange } = React.useContext(SideDialogContext);const handleClick = React.useCallback((e: React.MouseEvent<HTMLButtonElement>) => {onClick?.(e);onOpenChange(false);},[onClick, onOpenChange],);if (asChild && children) {return React.cloneElement(children as React.ReactElement, {onClick: handleClick,...(props as React.HTMLAttributes<HTMLButtonElement>),});}return (<button data-slot='side-dialog-close' onClick={handleClick} {...props}>{children}</button>);}
Loading...
Contextual positioning
Unlike traditional centered dialogs, SideDialog provides a more contextual user experience by opening from the edges of the screen, perfect for notifications, user menus, and contextual actions.
Built-in features
- 6 positions: Choose from
top-left,top-right,bottom-left,bottom-right,left,right - Flexible sizes: Predefined sizes (
xs,sm,md,lg,xl,2xl) - Lightweight: Custom implementation without depending on shadcn dialog
- Accessible: Keyboard support (Escape), focus management, and ARIA compliance
- Portal rendering: Renders in
document.bodyto avoid z-index issues - Smooth animations: Entry/exit animations with Tailwind CSS
- Controlled & Uncontrolled: Works with or without state management
Position options
The position prop controls where the dialog appears:
// Corner positions
<SideDialog position="top-left"> {/* Top left corner */}
<SideDialog position="top-right"> {/* Top right corner */}
<SideDialog position="bottom-left"> {/* Bottom left corner */}
<SideDialog position="bottom-right"> {/* Bottom right corner (default) */}
// Side positions (vertically centered)
<SideDialog position="left"> {/* Left side, centered */}
<SideDialog position="right"> {/* Right side, centered */}Size options
The size prop controls the dialog size with predefined responsive values:
// Predefined sizes (all automatically responsive)
<SideDialog size="xs"> {/* 320px max-width, adapts to small screens */}
<SideDialog size="sm"> {/* 384px max-width (default) */}
<SideDialog size="md"> {/* 448px max-width */}
<SideDialog size="lg"> {/* 512px max-width */}
<SideDialog size="xl"> {/* 576px max-width */}
<SideDialog size="2xl"> {/* 672px max-width */}
// For custom sizes, use className with responsive constraints
<SideDialogContent className="w-full max-w-[min(400px,calc(100vw-2rem))]">
{/* Custom size with responsive protection */}
</SideDialogContent>Responsive behavior
All predefined sizes automatically include responsive constraints using CSS min() function:
- Desktop: Uses the specified max-width (e.g.
lg= 32rem/512px) - Small screens: Automatically reduces to
calc(100vw - 2rem)when viewport is smaller - Never overflows: The dialog will always fit within the viewport with 1rem margin on each side
- CSS native: Uses
min(targetSize, calc(100vw - 2rem))for optimal performance
Common use cases
Notifications and alerts
// Success notification - top right
<SideDialog position="top-right">
<SideDialogTrigger asChild>
<Button onClick={handleSave}>Save Changes</Button>
</SideDialogTrigger>
<SideDialogContent>
<SideDialogHeader>
<SideDialogTitle className="text-green-600">Success!</SideDialogTitle>
<SideDialogDescription>Your changes have been saved.</SideDialogDescription>
</SideDialogHeader>
</SideDialogContent>
</SideDialog>
// Error notification - top left
<SideDialog position="top-left">
<SideDialogContent>
<SideDialogHeader>
<SideDialogTitle className="text-red-600">Error</SideDialogTitle>
<SideDialogDescription>Something went wrong. Please try again.</SideDialogDescription>
</SideDialogHeader>
</SideDialogContent>
</SideDialog>Different sizes for different content
// Small notification - xs size
<SideDialog position="top-right" size="xs">
<SideDialogContent>
<SideDialogHeader>
<SideDialogTitle>Saved!</SideDialogTitle>
</SideDialogHeader>
</SideDialogContent>
</SideDialog>
// User profile - medium size
<SideDialog position="top-right" size="md">
<SideDialogContent>
<SideDialogHeader>
<SideDialogTitle>User Profile</SideDialogTitle>
</SideDialogHeader>
<div className="space-y-4">
<div className="flex items-center space-x-4">
<Avatar className="w-16 h-16">
<AvatarImage src="/avatar.jpg" />
<AvatarFallback>JD</AvatarFallback>
</Avatar>
<div>
<h3 className="font-semibold">John Doe</h3>
<p className="text-muted-foreground">john@example.com</p>
</div>
</div>
</div>
</SideDialogContent>
</SideDialog>
// Wide content panel - custom size with responsive constraints
<SideDialog position="right">
<SideDialogContent className="w-full max-w-[min(600px,calc(100vw-2rem))]">
<SideDialogHeader>
<SideDialogTitle>Content Editor</SideDialogTitle>
</SideDialogHeader>
<div className="space-y-4">
<Textarea placeholder="Write your content here..." rows={10} />
<div className="flex justify-end space-x-2">
<Button variant="outline">Save Draft</Button>
<Button>Publish</Button>
</div>
</div>
</SideDialogContent>
</SideDialog>Help and information
// Help panel - bottom left
<SideDialog position="bottom-left">
<SideDialogTrigger asChild>
<Button variant="outline" size="icon">
<HelpCircle className="size-4" />
</Button>
</SideDialogTrigger>
<SideDialogContent>
<SideDialogHeader>
<SideDialogTitle>Need Help?</SideDialogTitle>
<SideDialogDescription>
Find answers to common questions or contact support.
</SideDialogDescription>
</SideDialogHeader>
<div className="space-y-3">
<Button variant="outline" className="w-full">
<Book className="mr-2 size-4" />
Documentation
</Button>
<Button variant="outline" className="w-full">
<MessageCircle className="mr-2 size-4" />
Contact Support
</Button>
</div>
</SideDialogContent>
</SideDialog>State management
The component supports both controlled and uncontrolled modes, automatically detecting which mode to use based on the props provided:
// Uncontrolled: Component manages its own state
<SideDialog position="top-right">
<SideDialogTrigger asChild>
<Button>Open</Button>
</SideDialogTrigger>
<SideDialogContent>
<SideDialogHeader>
<SideDialogTitle>Self-managed State</SideDialogTitle>
</SideDialogHeader>
</SideDialogContent>
</SideDialog>
// Controlled: You manage the state
const [open, setOpen] = useState(false);
<SideDialog open={open} onOpenChange={setOpen} position="top-right">
<SideDialogTrigger asChild>
<Button>Open</Button>
</SideDialogTrigger>
<SideDialogContent>
<SideDialogHeader>
<SideDialogTitle>Controlled State</SideDialogTitle>
</SideDialogHeader>
</SideDialogContent>
</SideDialog>Advanced customization
Custom close behavior
// Disable the default close button
<SideDialogContent showCloseButton={false}>
<SideDialogHeader>
<SideDialogTitle>Custom Controls</SideDialogTitle>
</SideDialogHeader>
<SideDialogFooter>
<SideDialogClose asChild>
<Button variant="destructive">Cancel</Button>
</SideDialogClose>
<SideDialogClose asChild>
<Button>Confirm</Button>
</SideDialogClose>
</SideDialogFooter>
</SideDialogContent>Custom styling
// Custom dialog appearance
<SideDialogContent className="border-primary shadow-xl bg-gradient-to-b from-background to-muted">
<SideDialogHeader>
<SideDialogTitle className="text-primary">Custom Style</SideDialogTitle>
</SideDialogHeader>
</SideDialogContent>Examples
Default
Loading...
'use client';import { useState } from 'react';import { Button } from '@/components/ui/button';import {SideDialog,SideDialogClose,SideDialogContent,SideDialogDescription,SideDialogFooter,SideDialogHeader,SideDialogTitle,SideDialogTrigger,} from '@/components/ui/shuip/side-dialog';export default function SideDialogExample() {const [open, setOpen] = useState(false);return (<div className='flex flex-wrap gap-4 p-8'><SideDialog open={open} onOpenChange={setOpen}><SideDialogTrigger asChild><Button variant='outline'>Open Dialog (Bottom Right)</Button></SideDialogTrigger><SideDialogContent><SideDialogHeader><SideDialogTitle>Side Dialog Example</SideDialogTitle><SideDialogDescription>This is a side dialog that opens on the bottom right by default. On mobile devices, it automaticallybecomes a drawer.</SideDialogDescription></SideDialogHeader><div className='py-4'><p className='text-sm text-muted-foreground'>Content goes here. This dialog is responsive and will adapt to mobile devices by showing as a drawerinstead.</p></div><SideDialogFooter><SideDialogClose asChild><Button variant='outline'>Cancel</Button></SideDialogClose><Button>Save changes</Button></SideDialogFooter></SideDialogContent></SideDialog><SideDialog position='top-left'><SideDialogTrigger asChild><Button variant='outline'>Top Left</Button></SideDialogTrigger><SideDialogContent><SideDialogHeader><SideDialogTitle>Top Left Dialog</SideDialogTitle><SideDialogDescription>This dialog opens from the top left corner.</SideDialogDescription></SideDialogHeader><div className='py-4'><p className='text-sm'>Perfect for notifications or quick actions.</p></div></SideDialogContent></SideDialog><SideDialog position='top-right'><SideDialogTrigger asChild><Button variant='outline'>Top Right</Button></SideDialogTrigger><SideDialogContent><SideDialogHeader><SideDialogTitle>Top Right Dialog</SideDialogTitle><SideDialogDescription>This dialog opens from the top right corner.</SideDialogDescription></SideDialogHeader><div className='py-4'><p className='text-sm'>Great for user menus or profile settings.</p></div></SideDialogContent></SideDialog><SideDialog position='bottom-left'><SideDialogTrigger asChild><Button variant='outline'>Bottom Left</Button></SideDialogTrigger><SideDialogContent><SideDialogHeader><SideDialogTitle>Bottom Left Dialog</SideDialogTitle><SideDialogDescription>This dialog opens from the bottom left corner.</SideDialogDescription></SideDialogHeader><div className='py-4'><p className='text-sm'>Ideal for help or support content.</p></div></SideDialogContent></SideDialog><SideDialog position='left'><SideDialogTrigger asChild><Button variant='outline'>Left Side</Button></SideDialogTrigger><SideDialogContent><SideDialogHeader><SideDialogTitle>Left Side Dialog</SideDialogTitle><SideDialogDescription>This dialog opens from the left side, centered vertically.</SideDialogDescription></SideDialogHeader><div className='py-4'><p className='text-sm'>Perfect for navigation or filters.</p></div></SideDialogContent></SideDialog><SideDialog position='right'><SideDialogTrigger asChild><Button variant='outline'>Right Side</Button></SideDialogTrigger><SideDialogContent><SideDialogHeader><SideDialogTitle>Right Side Dialog</SideDialogTitle><SideDialogDescription>This dialog opens from the right side, centered vertically.</SideDialogDescription></SideDialogHeader><div className='py-4'><p className='text-sm'>Great for sidebars or additional information.</p></div><SideDialogFooter><SideDialogClose asChild><Button variant='outline'>Close</Button></SideDialogClose></SideDialogFooter></SideDialogContent></SideDialog><SideDialog><SideDialogTrigger asChild><Button variant='outline'>No Close Button</Button></SideDialogTrigger><SideDialogContent showCloseButton={false}><SideDialogHeader><SideDialogTitle>Custom Close</SideDialogTitle><SideDialogDescription>This dialog doesn't show the default close button.</SideDialogDescription></SideDialogHeader><div className='py-4'><p className='text-sm'>You can control closing with custom buttons only.</p></div><SideDialogFooter><SideDialogClose asChild><Button>Done</Button></SideDialogClose></SideDialogFooter></SideDialogContent></SideDialog>{/* Different Sizes */}<SideDialog size='xs'><SideDialogTrigger asChild><Button variant='outline'>Extra Small (xs)</Button></SideDialogTrigger><SideDialogContent><SideDialogHeader><SideDialogTitle>XS Size</SideDialogTitle><SideDialogDescription>Extra small dialog size (320px max).</SideDialogDescription></SideDialogHeader></SideDialogContent></SideDialog><SideDialog size='md' position='top-left'><SideDialogTrigger asChild><Button variant='outline'>Medium (md)</Button></SideDialogTrigger><SideDialogContent><SideDialogHeader><SideDialogTitle>MD Size</SideDialogTitle><SideDialogDescription>Medium dialog size (448px max).</SideDialogDescription></SideDialogHeader></SideDialogContent></SideDialog><SideDialog size='xl' position='left'><SideDialogTrigger asChild><Button variant='outline'>Extra Large (xl)</Button></SideDialogTrigger><SideDialogContent><SideDialogHeader><SideDialogTitle>XL Size</SideDialogTitle><SideDialogDescription>Extra large dialog size (576px max).</SideDialogDescription></SideDialogHeader></SideDialogContent></SideDialog><SideDialog size='2xl' position='bottom-left'><SideDialogTrigger asChild><Button variant='outline'>2XL Size</Button></SideDialogTrigger><SideDialogContent><SideDialogHeader><SideDialogTitle>2XL Size</SideDialogTitle><SideDialogDescription>Extra extra large dialog size (672px max).</SideDialogDescription></SideDialogHeader></SideDialogContent></SideDialog><SideDialog position='right'><SideDialogTrigger asChild><Button variant='outline'>Custom 400px</Button></SideDialogTrigger><SideDialogContent className='w-full max-w-[min(400px,calc(100vw-2rem))]'><SideDialogHeader><SideDialogTitle>Custom Size</SideDialogTitle><SideDialogDescription>Custom size of 400px with responsive constraints.</SideDialogDescription></SideDialogHeader></SideDialogContent></SideDialog><SideDialog position='bottom-left'><SideDialogTrigger asChild><Button variant='outline'>50% Viewport</Button></SideDialogTrigger><SideDialogContent className='w-full max-w-[min(50vw,calc(100vw-2rem))]'><SideDialogHeader><SideDialogTitle>Viewport Size</SideDialogTitle><SideDialogDescription>50% of viewport width with responsive constraints.</SideDialogDescription></SideDialogHeader></SideDialogContent></SideDialog><SideDialog><SideDialogTrigger asChild><Button variant='outline' className='bg-gradient-to-r from-blue-300 to-purple-300'>Colored</Button></SideDialogTrigger><SideDialogContent className='bg-gradient-to-r from-blue-300 to-purple-300'><SideDialogHeader><SideDialogTitle>Colored</SideDialogTitle><SideDialogDescription>Colored dialog.</SideDialogDescription></SideDialogHeader></SideDialogContent></SideDialog></div>);}
Form
Loading...
'use client';import { zodResolver } from '@hookform/resolvers/zod';import { useState } from 'react';import { useForm } from 'react-hook-form';import { z } from 'zod';import { Button } from '@/components/ui/button';import { Form } from '@/components/ui/form';import { InputField } from '@/components/ui/shuip/react-hook-form/input-field';import {SideDialog,SideDialogClose,SideDialogContent,SideDialogDescription,SideDialogFooter,SideDialogHeader,SideDialogTitle,SideDialogTrigger,} from '@/components/ui/shuip/side-dialog';import { SubmitButton } from '@/components/ui/shuip/submit-button';const formSchema = z.object({name: z.string().min(1, 'Name is required'),});export default function SideDialogSimpleExample() {const [open, setOpen] = useState(false);const form = useForm({resolver: zodResolver(formSchema),});const onSubmit = async (data: z.infer<typeof formSchema>) => {await new Promise((resolve) => setTimeout(resolve, 1500));alert(`Form submitted: ${JSON.stringify(data)}`);setOpen(false);form.reset();};return (<SideDialog open={open} onOpenChange={setOpen}><SideDialogTrigger asChild><Button variant='outline'>Open Form Side Dialog</Button></SideDialogTrigger><SideDialogContent><Form {...form}><form onSubmit={form.handleSubmit(onSubmit)}><SideDialogHeader><SideDialogTitle>Form Side Dialog</SideDialogTitle><SideDialogDescription>This is a form side dialog.</SideDialogDescription></SideDialogHeader><div className='space-y-4'><InputField register={form.register('name')} label='Name' description='This is a description' /></div><SideDialogFooter><SideDialogClose asChild><Button variant='outline'>Cancel</Button></SideDialogClose><SubmitButton className='w-fit'>Submit</SubmitButton></SideDialogFooter></form></Form></SideDialogContent></SideDialog>);}
Props
Prop
Type
SideDialogContent Props
Prop
Type
SideDialogTrigger Props
Prop
Type