Skip to main content

Modal

Modal is an overlay that focuses people on a task above the page, such as confirming changes or renaming a project.

Import

import { Modal } from '@vortexlabs/vortex';
import type { ModalProps } from '@vortexlabs/vortex';

Allied components

Other links

"use client";

import type { ModalProps } from "@vortexlabs/vortex";
import {
  Button,
  Modal,
  Placeholder,
  Stack,
  useSwitch,
} from "@vortexlabs/vortex";

type Props = Omit<ModalProps, "active" | "onClose"> & {
  label?: string;
  title?: string;
};

export const ModalExample = (props: Props) => {
  const {
    label = "Open modal",
    title = "Modal title",
    children,
    ...modalProps
  } = props;
  const { isOn, turnOn, turnOff } = useSwitch();

  return (
    <>
      <Button size="sm" onClick={turnOn}>
        {label}
      </Button>
      <Modal {...modalProps} active={isOn} onClose={turnOff}>
        <Stack gap={4}>
          <Modal.Dismissible closeAriaLabel="Close modal">
            <Modal.Title>{title}</Modal.Title>
          </Modal.Dismissible>
          <Modal.Description>Modal description.</Modal.Description>
          {children ?? <Placeholder height={16} />}
        </Stack>
      </Modal>
    </>
  );
};

export const Example = () => <ModalExample />;

Features

  • Blocks interaction behind the dialog.
  • Connects the dialog to its title and description.
  • Dims, blurs, or keeps the backdrop clear.
  • Keeps focus inside and returns it on close.

Modal.Dismissible

Wrap Modal.Title in Modal.Dismissible to align the close button. Without children, it sits in the top corner.

export const ModalDismissibleExample = () => {
  const { isOn, turnOn, turnOff } = useSwitch();

  return (
    <Stack direction="row" gap={3} justify="center">
      <ModalExample
        label="Open long title"
        width="24rem"
        title="Review the changes to your project settings before saving them"
      />
      <Button size="sm" onClick={turnOn}>
        Open standalone
      </Button>
      <Modal active={isOn} onClose={turnOff} padding={{ xs: 4, md: 8 }}>
        <Modal.Dismissible closeAriaLabel="Close modal" />
        <Stack gap={4}>
          <Modal.Title>Modal title</Modal.Title>
          <Placeholder height={16} />
        </Stack>
      </Modal>
    </Stack>
  );
};

Variants

Use variant to dim, blur, or clear the backdrop. A transparent backdrop still blocks interaction.

export const ModalVariantExample = () => (
  <Stack direction="row" gap={3} justify="center">
    <ModalExample variant="dim" label="Open dim" />
    <ModalExample variant="blurred" label="Open blurred" />
    <ModalExample variant="transparent" label="Open transparent" />
  </Stack>
);

Outside Clicks

Set disableCloseOnOutsideClick to ignore backdrop clicks. Escape and the close button still request dismissal.

<ModalExample disableCloseOnOutsideClick />

Size and Padding

Set width and padding with breakpoint objects to adjust the panel across screen sizes.

export const ModalSizeExample = () => (
  <Stack direction="row" gap={3} justify="center">
    <ModalExample width="20rem" padding={3} label="Open compact" />
    <ModalExample width="40rem" padding={6} label="Open spacious" />
    <ModalExample
      width={{ xs: "90%", sm: "36rem", md: "48rem" }}
      padding={{ xs: 3, md: 6 }}
      label="Open responsive"
    />
  </Stack>
);

Accessibility

Key
Description
TabShiftTab
Moves focus between controls inside the modal.
Escape
Calls onClose with escape-key.
Use Modal.Title or ariaLabel to name the dialog.
Set closeAriaLabel on Modal.Dismissible.