# Modal

> Cherry Modal component - an animated dialog rendered in a portal with Escape and outside-click dismissal.

Source: https://cherry.al/code/modal

> For the complete documentation index, see [llms.txt](https://cherry.al/llms.txt).

# Modal

<iframe className="light-only" src="https://demo.cherry.al/preview/modal?theme=light" title="Modal" loading="lazy" style={{ width: "100%", height: "320px", border: "1px solid var(--color-grayLight)", borderRadius: "12px" }} />
<iframe className="dark-only" src="https://demo.cherry.al/preview/modal?theme=dark" title="Modal" loading="lazy" style={{ width: "100%", height: "320px", border: "1px solid var(--color-grayLight)", borderRadius: "12px" }} />

The Modal component renders an animated dialog on top of the page. It portals into `document.body` so the overlay always sits above transformed or clipped ancestors, and it closes on Escape, on a click outside, or with the built-in close button.

The Modal is a controlled component: the parent owns the open state. The dialog surface carries `role="dialog"` and `aria-modal="true"`, and takes its accessible name from `$title`.

For design guidelines and all interactive states, see the [Overlays](/overlays) design page.

```jsx
"use client";
import React, { useState } from "react";
import { Button, Modal } from "cherry-styled-components";

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <Button onClick={() => setIsOpen(true)}>Open Modal</Button>
      <Modal
        $isOpen={isOpen}
        $onClose={() => setIsOpen(false)}
        $title="Modal Title"
      >
        Modal content goes here.
      </Modal>
    </>
  );
}
```

With a custom width for larger content:

```html
<Modal $isOpen={isOpen} $onClose={close} $width={800}>
  Content
</Modal>
```

## Restyling

To restyle the modal from your app, pass a `className` (or `style`). Both are applied to the overlay root, and the inner parts expose stable class hooks: `.modal-inner`, `.modal-close`, `.modal-title`, and `.modal-content`.

```html
<Modal $isOpen={isOpen} $onClose={close} className="checkout-modal">
  Content
</Modal>
```

```css
.checkout-modal .modal-inner {
  border-radius: 24px;
}
```

<Callout type="warning">
  Wrapping with `styled(Modal)` does not work: styled-components strips
  transient `$`-props before they reach the wrapped component, so the modal
  never receives `$isOpen` or `$onClose`. Target a passed class instead.
</Callout>

## Properties

<Field value="children" type="React.ReactNode">
  The modal content. It scrolls internally when taller than the viewport.
</Field>

<Field value="$isOpen" type="boolean" required>
  Whether the modal is visible. The modal animates in and out on change.
</Field>

<Field value="$onClose" type="() => void" required>
  Called when the user dismisses the modal (Escape, outside click, or the
  close button).
</Field>

<Field value="$title" type="string">
  Optional title rendered with a divider below it. Also used as the dialog's
  `aria-label`.
</Field>

<Field value="$width" type="number">
  Maximum width in pixels on large screens. Falls back to `500px` when unset.
</Field>

<Field value="$hideCloseButton" type="boolean">
  Omits the built-in close button in the top-right corner. Escape and outside
  click still dismiss the modal.
</Field>

<Field value="className" type="string">
  Applied to the overlay root, for app-level restyling. See the Restyling
  section above.
</Field>

<Field value="style" type="React.CSSProperties">
  Inline styles applied to the overlay root.
</Field>

<Callout type="note">
  The Modal is SSR-safe: it renders nothing on the server and during the first
  client render, then portals once hydration has completed.
</Callout>

<Callout type="note">
  The modal unmounts its children once the exit animation finishes, so state
  inside the modal (such as form inputs) resets between openings. To keep
  state across openings, lift it into the parent.
</Callout>

<Button href="https://github.com/cherry-design-system/styled-components/blob/main/src/lib/modal.tsx" icon="code" iconPosition="left">
  View Source
</Button>
