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 state
const 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 mode
const [internalOpen, setInternalOpen] = React.useState(false);
// Use controlled props if provided, otherwise use internal state
const 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(
<div
data-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}
>
<div
data-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 (
<div
data-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.body to 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 automatically
becomes 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 drawer
instead.
</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

On this page