Modal is a floating surface used to display transient content such as confirmation actions, selection options, and more.
In the Figma library, Modal is configured with variants: Type (Error / Warning / Information / Success), Alignment , and a Footer style (Basic / Checkbox / Information / Toggle). In code these are not separate props; you compose them from the Modal parts (Modal.Root, Trigger, Content, Header, Body, Footer):
Type (Error / Warning / Information / Success) : pass the matching icon (and color) to Modal.Header via its icon prop, or render a fully custom header through children.
Footer (Basic / Checkbox / Information / Toggle) : place whatever the variant needs inside Modal.Footer: action buttons, a Checkbox, a Switch, or helper text.
Alignment : handled with layout classes on Modal.Body / Modal.Footer.
The Header icon / title / description props are only a shortcut for the common case; anything beyond that is done by composition, so every Figma variant is achievable.
Install the following dependencies: npm install @radix-ui/react-dialog
Create a modal.tsx file and paste the following code into it. // AlignUI Modal v0.0.0
import * as React from 'react' ;
import * as DialogPrimitive from '@radix-ui/react-dialog' ;
import { RiCloseLine , type RemixiconComponentType } from '@remixicon/react' ;
import * as CompactButton from '@/components/ui/compact-button' ;
import { cn } from '@/utils/cn' ;
const ModalRoot = DialogPrimitive .Root;
const ModalTrigger = DialogPrimitive .Trigger;
const ModalClose = DialogPrimitive .Close;
const ModalPortal = DialogPrimitive .Portal;
const ModalOverlay = React .forwardRef <
React .ComponentRef <typeof DialogPrimitive .Overlay > ,
React .ComponentPropsWithoutRef <typeof DialogPrimitive .Overlay >
> (({ className , ... rest } , forwardedRef) => {
return (
< DialogPrimitive.Overlay
ref = {forwardedRef}
className = { cn (
// base
'fixed inset-0 z-50 flex flex-col items-center justify-center overflow-y-auto bg-overlay p-4 backdrop-blur-[10px]' ,
// animation
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0' ,
className ,
)}
{ ... rest}
/>
);
});
ModalOverlay .displayName = 'ModalOverlay' ;
const ModalContent = React .forwardRef <
React .ComponentRef <typeof DialogPrimitive .Content > ,
React .ComponentPropsWithoutRef <typeof DialogPrimitive .Content > & {
overlayClassName ?: string;
showClose ?: boolean;
}
> (
(
{ className , overlayClassName , children , showClose = true , ... rest } ,
forwardedRef ,
) => {
return (
< ModalPortal >
< ModalOverlay className = {overlayClassName}>
< DialogPrimitive.Content
ref = {forwardedRef}
className = { cn (
// base
'relative w-full max-w-[400px]' ,
'rounded-20 bg-bg-white-0 shadow-regular-md' ,
// focus
'focus:outline-none' ,
// animation
'data-[state=open]:animate-in data-[state=closed]:animate-out' ,
'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0' ,
'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95' ,
className ,
)}
{ ... rest}
>
{children}
{showClose && (
< ModalClose asChild >
< CompactButton.Root
variant = 'ghost'
size = 'large'
className = 'absolute right-4 top-4'
>
< CompactButton.Icon as = {RiCloseLine} />
</ CompactButton.Root >
</ ModalClose >
)}
</ DialogPrimitive.Content >
</ ModalOverlay >
</ ModalPortal >
);
} ,
);
ModalContent .displayName = 'ModalContent' ;
function ModalHeader ({
className ,
children ,
icon: Icon ,
title ,
description ,
... rest
} : React . HTMLAttributes < HTMLDivElement > & {
icon ?: RemixiconComponentType ;
title ?: string ;
description ?: string ;
}) {
return (
< div
className = { cn (
'relative flex items-start gap-3.5 py-4 pl-5 pr-14 before:absolute before:inset-x-0 before:bottom-0 before:border-b before:border-stroke-soft-200' ,
className ,
)}
{ ... rest}
>
{children || (
<>
{Icon && (
< div className = 'flex size-10 shrink-0 items-center justify-center rounded-full bg-bg-white-0 ring-1 ring-inset ring-stroke-soft-200' >
< Icon className = 'size-5 text-text-sub-600' />
</ div >
)}
{(title || description) && (
< div className = 'flex-1 space-y-1' >
{title && < ModalTitle >{title}</ ModalTitle >}
{description && (
< ModalDescription >{description}</ ModalDescription >
)}
</ div >
)}
</>
)}
</ div >
);
}
ModalHeader .displayName = 'ModalHeader' ;
const ModalTitle = React .forwardRef <
React .ComponentRef <typeof DialogPrimitive .Title > ,
React .ComponentPropsWithoutRef <typeof DialogPrimitive .Title >
> (({ className , ... rest } , forwardedRef) => {
return (
< DialogPrimitive.Title
ref = {forwardedRef}
className = { cn ( 'text-label-sm text-text-strong-950' , className)}
{ ... rest}
/>
);
});
ModalTitle .displayName = 'ModalTitle' ;
const ModalDescription = React .forwardRef <
React .ComponentRef <typeof DialogPrimitive .Description > ,
React .ComponentPropsWithoutRef <typeof DialogPrimitive .Description >
> (({ className , ... rest } , forwardedRef) => {
return (
< DialogPrimitive.Description
ref = {forwardedRef}
className = { cn ( 'text-paragraph-xs text-text-sub-600' , className)}
{ ... rest}
/>
);
});
ModalDescription .displayName = 'ModalDescription' ;
function ModalBody ({
className ,
... rest
} : React . HTMLAttributes < HTMLDivElement >) {
return < div className = { cn ( 'p-5' , className)} { ... rest} />;
}
ModalBody .displayName = 'ModalBody' ;
function ModalFooter ({
className ,
... rest
} : React . HTMLAttributes < HTMLDivElement >) {
return (
< div
className = { cn (
'flex items-center justify-between gap-3 border-t border-stroke-soft-200 px-5 py-4' ,
className ,
)}
{ ... rest}
/>
);
}
ModalFooter .displayName = 'ModalFooter' ;
export {
ModalRoot as Root ,
ModalTrigger as Trigger ,
ModalClose as Close ,
ModalPortal as Portal ,
ModalOverlay as Overlay ,
ModalContent as Content ,
ModalHeader as Header ,
ModalTitle as Title ,
ModalDescription as Description ,
ModalBody as Body ,
ModalFooter as Footer ,
}; Update the import paths to match your project setup.
This component is based on the Radix UI Dialog primitives. Refer to their documentation for the API reference.
This component is based on the Dialog.Root primitive.
This component is based on the Dialog.Trigger primitive.
This component is based on the Dialog.Close primitive.
This component is based on the Dialog.Portal primitive.
This component is based on the Dialog.Overlay primitive.
This component is based on the Dialog.Content primitive. Wrapped by Portal and Overlay.
Used to display the header section of a modal dialog. It accepts icon, title, description if children is not provided.
This component is based on the <div> element and supports all of its props. And adds:
Prop Type Default RemixiconComponentType
string
string
This component is based on the Dialog.Title primitive.
This component is based on the Dialog.Description primitive.
Used for the main content area of a modal dialog.
This component is based on the <div> element and supports all of its props.
Used for the footer section of a modal dialog. It typically contains action buttons like "Save" or "Cancel."
This component is based on the <div> element and supports all of its props.
© 2026 AlignUI Design System. All rights reserved.