> ## Documentation Index
> Fetch the complete documentation index at: https://prefab.prefect.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Dialog

> Modal overlay with a trigger and content panel.

export const ComponentPreview = ({json, height, resizable, bare, hideJson, playground, children}) => {
  const hostRef = React.useRef(null);
  const handleRef = React.useRef(null);
  const cardRef = React.useRef(null);
  const containerRef = React.useRef(null);
  const [previewWidth, setPreviewWidth] = React.useState(null);
  const [isDragging, setIsDragging] = React.useState(false);
  const [playgroundHref, setPlaygroundHref] = React.useState(null);
  React.useEffect(function () {
    if (!playground) return;
    var isLocal = location.hostname === "localhost" || location.hostname === "127.0.0.1";
    var path = isLocal ? "/playground" : "/docs/playground";
    var url = new URL(path, window.location.href);
    url.hash = "code=" + playground;
    setPlaygroundHref(url.href);
  }, [playground]);
  var jsonStr = typeof json === "string" ? json : JSON.stringify(json, null, 2);
  React.useEffect(function () {
    if (cardRef.current) {
      var card = cardRef.current.closest(".group");
      if (card) card.classList.remove("group");
    }
    var host = hostRef.current;
    if (!host || !json) return;
    function mount() {
      if (!window.__prefab || !host) return;
      var dark = document.documentElement.classList.contains("dark");
      handleRef.current = window.__prefab.mountPreview(host, jsonStr, {
        dark: dark
      });
      if (bare && host.shadowRoot) {
        var m = host.shadowRoot.querySelector("[data-prefab-mount]");
        if (m) m.style.background = "transparent";
      }
    }
    if (window.__prefab) {
      mount();
    } else {
      if (!window.__prefabLoading) {
        if (!window.__prefabReady) {
          var s = document.createElement("script");
          s.src = "/renderer.js";
          document.head.appendChild(s);
        }
        window.__prefabLoading = new Promise(function (resolve) {
          function check() {
            if (window.__prefabReady) {
              window.__prefabReady.then(resolve);
            } else {
              setTimeout(check, 10);
            }
          }
          check();
        });
      }
      window.__prefabLoading.then(mount);
    }
    var observer = new MutationObserver(function () {
      var dark = document.documentElement.classList.contains("dark");
      if (handleRef.current) handleRef.current.setDark(dark);
    });
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["class"]
    });
    return function () {
      observer.disconnect();
      if (handleRef.current) {
        handleRef.current.unmount();
        handleRef.current = null;
      }
    };
  }, [json]);
  function startResize(e) {
    e.preventDefault();
    var startX = e.clientX;
    var startW = hostRef.current ? hostRef.current.offsetWidth : 0;
    var maxW = containerRef.current ? containerRef.current.offsetWidth - 10 : startW;
    setIsDragging(true);
    function onMove(ev) {
      var w = Math.max(260, Math.min(startW + (ev.clientX - startX), maxW));
      setPreviewWidth(w);
    }
    function onUp() {
      document.removeEventListener("mousemove", onMove);
      document.removeEventListener("mouseup", onUp);
      setIsDragging(false);
    }
    document.addEventListener("mousemove", onMove);
    document.addEventListener("mouseup", onUp);
  }
  if (bare) {
    return <div ref={hostRef} />;
  }
  if (resizable) {
    return <Card>
        <div ref={cardRef} />
        <div ref={containerRef} style={{
      display: "flex",
      position: "relative",
      overflow: "hidden"
    }}>
          <div ref={hostRef} style={{
      flex: previewWidth ? "none" : 1,
      width: previewWidth ? previewWidth + "px" : undefined,
      minWidth: 0
    }} />
          <div onMouseDown={startResize} style={{
      width: "14px",
      flexShrink: 0,
      cursor: "col-resize",
      background: isDragging ? "var(--border, #e5e7eb)" : "color-mix(in srgb, var(--border, #e5e7eb) 40%, transparent)",
      display: "flex",
      alignItems: "center",
      justifyContent: "center",
      transition: "background 0.15s",
      userSelect: "none"
    }}>
            <svg width="6" height="24" style={{
      opacity: 0.5
    }}>
              <circle cx="2" cy="4" r="1.2" fill="currentColor" />
              <circle cx="2" cy="9" r="1.2" fill="currentColor" />
              <circle cx="2" cy="14" r="1.2" fill="currentColor" />
              <circle cx="2" cy="19" r="1.2" fill="currentColor" />
              <circle cx="5" cy="4" r="1.2" fill="currentColor" />
              <circle cx="5" cy="9" r="1.2" fill="currentColor" />
              <circle cx="5" cy="14" r="1.2" fill="currentColor" />
              <circle cx="5" cy="19" r="1.2" fill="currentColor" />
            </svg>
          </div>
          <div style={{
      flex: previewWidth ? 1 : "0 0 24px",
      minWidth: "24px",
      background: "repeating-linear-gradient(-45deg, transparent, transparent 3px, var(--border, #e5e7eb) 3px, var(--border, #e5e7eb) 4px)",
      opacity: 0.4
    }} />
          {previewWidth && <div style={{
      position: "absolute",
      bottom: "8px",
      right: "8px",
      background: "var(--background, white)",
      border: "1px solid var(--border, #e5e7eb)",
      borderRadius: "4px",
      padding: "1px 6px",
      fontSize: "11px",
      fontFamily: "monospace",
      color: "var(--muted-foreground, #6b7280)",
      pointerEvents: "none"
    }}>
              {previewWidth}px
            </div>}
        </div>
        {playgroundHref && <div style={{
      display: "flex",
      justifyContent: "flex-end",
      padding: "4px 0 8px"
    }}>
            <a href={playgroundHref} target="_blank" rel="noopener noreferrer" style={{
      fontSize: "11px",
      opacity: 0.4,
      textDecoration: "none",
      textDecorationLine: "none",
      borderBottom: "none",
      boxShadow: "none",
      display: "inline-flex",
      alignItems: "center",
      gap: "3px",
      transition: "opacity 0.15s",
      fontWeight: 400
    }} onMouseOver={function (e) {
      e.currentTarget.style.opacity = "0.6";
    }} onMouseOut={function (e) {
      e.currentTarget.style.opacity = "0.4";
    }}>
              Edit in Playground
              <svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 3h6v6" /><path d="M10 14 21 3" /><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></svg>
            </a>
          </div>}
        <div style={{
      marginBottom: "-2rem"
    }}>{children}</div>
      </Card>;
  }
  return <Card>
      <div ref={cardRef} />
      <div ref={hostRef} />
      {playgroundHref && <div style={{
    display: "flex",
    justifyContent: "flex-end",
    padding: "4px 0 8px"
  }}>
          <a href={playgroundHref} target="_blank" rel="noopener noreferrer" style={{
    fontSize: "11px",
    opacity: 0.4,
    textDecoration: "none",
    textDecorationLine: "none",
    borderBottom: "none",
    boxShadow: "none",
    display: "inline-flex",
    alignItems: "center",
    gap: "3px",
    transition: "opacity 0.15s",
    fontWeight: 400
  }} onMouseOver={function (e) {
    e.currentTarget.style.opacity = "0.6";
  }} onMouseOut={function (e) {
    e.currentTarget.style.opacity = "0.4";
  }}>
            Edit in Playground
            <svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 3h6v6" /><path d="M10 14 21 3" /><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></svg>
          </a>
        </div>}
      <div style={{
    marginBottom: "-2rem"
  }}>{children}</div>
    </Card>;
};

Dialogs display content in a modal overlay that focuses user attention. The first child is the trigger element, the thing users click to open the dialog. Everything after the first child becomes the dialog body. An optional `title` and `description` render in the dialog header.

The dialog closes when the user clicks the X button, presses Escape, or clicks outside the overlay.

<Note>
  Clicking outside to dismiss may not work in the doc previews below due to how they're sandboxed. It works in deployed apps.
</Note>

## Basic Usage

<ComponentPreview height="400px" json={{"view":{"type":"Dialog","title":"Crew Profile","description":"Update your details for the Heart of Gold crew manifest.","dismissible":true,"children":[{"type":"Button","label":"Edit Profile","variant":"outline","size":"default","disabled":false},{"cssClass":"gap-3","type":"Column","children":[{"type":"Label","text":"Name","optional":false},{"name":"input_4","type":"Input","inputType":"text","placeholder":"Arthur Dent","disabled":false,"readOnly":false,"required":false},{"type":"Label","text":"Designation","optional":false},{"name":"input_5","type":"Input","inputType":"text","placeholder":"Sandwich Maker","disabled":false,"readOnly":false,"required":false}]},{"cssClass":"gap-2 justify-end","type":"Row","children":[{"type":"Button","label":"Save changes","variant":"default","size":"default","disabled":false,"onClick":{"action":"closeOverlay"}}]}]}}} playground="ZnJvbSBwcmVmYWJfdWkuY29tcG9uZW50cyBpbXBvcnQgKAogICAgQnV0dG9uLAogICAgQ29sdW1uLAogICAgRGlhbG9nLAogICAgSW5wdXQsCiAgICBMYWJlbCwKICAgIFJvdywKKQpmcm9tIHByZWZhYl91aS5hY3Rpb25zIGltcG9ydCBDbG9zZU92ZXJsYXkKCndpdGggRGlhbG9nKAogICAgdGl0bGU9IkNyZXcgUHJvZmlsZSIsCiAgICBkZXNjcmlwdGlvbj0iVXBkYXRlIHlvdXIgZGV0YWlscyBmb3IgdGhlIEhlYXJ0IG9mIEdvbGQgY3JldyBtYW5pZmVzdC4iLAopOgogICAgQnV0dG9uKCJFZGl0IFByb2ZpbGUiLCB2YXJpYW50PSJvdXRsaW5lIikKICAgIHdpdGggQ29sdW1uKGdhcD0zKToKICAgICAgICBMYWJlbCgiTmFtZSIpCiAgICAgICAgSW5wdXQocGxhY2Vob2xkZXI9IkFydGh1ciBEZW50IikKICAgICAgICBMYWJlbCgiRGVzaWduYXRpb24iKQogICAgICAgIElucHV0KHBsYWNlaG9sZGVyPSJTYW5kd2ljaCBNYWtlciIpCiAgICB3aXRoIFJvdyhnYXA9MiwgY3NzX2NsYXNzPSJqdXN0aWZ5LWVuZCIpOgogICAgICAgIEJ1dHRvbigiU2F2ZSBjaGFuZ2VzIiwgb25fY2xpY2s9Q2xvc2VPdmVybGF5KCkpCg">
  <CodeGroup>
    ```python Python icon="python" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    from prefab_ui.components import (
        Button,
        Column,
        Dialog,
        Input,
        Label,
        Row,
    )
    from prefab_ui.actions import CloseOverlay

    with Dialog(
        title="Crew Profile",
        description="Update your details for the Heart of Gold crew manifest.",
    ):
        Button("Edit Profile", variant="outline")
        with Column(gap=3):
            Label("Name")
            Input(placeholder="Arthur Dent")
            Label("Designation")
            Input(placeholder="Sandwich Maker")
        with Row(gap=2, css_class="justify-end"):
            Button("Save changes", on_click=CloseOverlay())
    ```

    ```json Protocol icon="brackets-curly" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    {
      "view": {
        "type": "Dialog",
        "title": "Crew Profile",
        "description": "Update your details for the Heart of Gold crew manifest.",
        "dismissible": true,
        "children": [
          {
            "type": "Button",
            "label": "Edit Profile",
            "variant": "outline",
            "size": "default",
            "disabled": false
          },
          {
            "cssClass": "gap-3",
            "type": "Column",
            "children": [
              {"type": "Label", "text": "Name", "optional": false},
              {
                "name": "input_4",
                "type": "Input",
                "inputType": "text",
                "placeholder": "Arthur Dent",
                "disabled": false,
                "readOnly": false,
                "required": false
              },
              {"type": "Label", "text": "Designation", "optional": false},
              {
                "name": "input_5",
                "type": "Input",
                "inputType": "text",
                "placeholder": "Sandwich Maker",
                "disabled": false,
                "readOnly": false,
                "required": false
              }
            ]
          },
          {
            "cssClass": "gap-2 justify-end",
            "type": "Row",
            "children": [
              {
                "type": "Button",
                "label": "Save changes",
                "variant": "default",
                "size": "default",
                "disabled": false,
                "onClick": {"action": "closeOverlay"}
              }
            ]
          }
        ]
      }
    }
    ```
  </CodeGroup>
</ComponentPreview>

## Confirmation Pattern

Dialogs work well for destructive actions that need explicit confirmation before proceeding. Use `CloseOverlay()` on the Cancel button to dismiss the dialog, and on the confirm button to close it after the action fires.

<ComponentPreview height="420px" json={{"view":{"type":"Dialog","title":"Delete Item","description":"This action cannot be undone.","dismissible":true,"children":[{"type":"Button","label":"Delete","variant":"destructive","size":"default","disabled":false},{"content":"Are you sure you want to permanently delete this item?","type":"Text"},{"cssClass":"gap-2 justify-end","type":"Row","children":[{"type":"Button","label":"Cancel","variant":"outline","size":"default","disabled":false,"onClick":{"action":"closeOverlay"}},{"type":"Button","label":"Confirm Delete","variant":"destructive","size":"default","disabled":false,"onClick":[{"action":"toolCall","tool":"delete_item","arguments":{"id":"{{ item_id }}"}},{"action":"closeOverlay"}]}]}]}}} playground="ZnJvbSBwcmVmYWJfdWkuY29tcG9uZW50cyBpbXBvcnQgQnV0dG9uLCBEaWFsb2csIFJvdywgVGV4dApmcm9tIHByZWZhYl91aS5hY3Rpb25zIGltcG9ydCBDbG9zZU92ZXJsYXkKZnJvbSBwcmVmYWJfdWkuYWN0aW9ucy5tY3AgaW1wb3J0IENhbGxUb29sCmZyb20gcHJlZmFiX3VpLnJ4IGltcG9ydCBSeAoKaXRlbV9pZCA9IFJ4KCJpdGVtX2lkIikKCndpdGggRGlhbG9nKHRpdGxlPSJEZWxldGUgSXRlbSIsIGRlc2NyaXB0aW9uPSJUaGlzIGFjdGlvbiBjYW5ub3QgYmUgdW5kb25lLiIpOgogICAgQnV0dG9uKCJEZWxldGUiLCB2YXJpYW50PSJkZXN0cnVjdGl2ZSIpCiAgICBUZXh0KCJBcmUgeW91IHN1cmUgeW91IHdhbnQgdG8gcGVybWFuZW50bHkgZGVsZXRlIHRoaXMgaXRlbT8iKQogICAgd2l0aCBSb3coZ2FwPTIsIGNzc19jbGFzcz0ianVzdGlmeS1lbmQiKToKICAgICAgICBCdXR0b24oIkNhbmNlbCIsIHZhcmlhbnQ9Im91dGxpbmUiLCBvbl9jbGljaz1DbG9zZU92ZXJsYXkoKSkKICAgICAgICBCdXR0b24oCiAgICAgICAgICAgICJDb25maXJtIERlbGV0ZSIsCiAgICAgICAgICAgIHZhcmlhbnQ9ImRlc3RydWN0aXZlIiwKICAgICAgICAgICAgb25fY2xpY2s9WwogICAgICAgICAgICAgICAgQ2FsbFRvb2woImRlbGV0ZV9pdGVtIiwgYXJndW1lbnRzPXsiaWQiOiBpdGVtX2lkfSksCiAgICAgICAgICAgICAgICBDbG9zZU92ZXJsYXkoKSwKICAgICAgICAgICAgXSwKICAgICAgICApCg">
  <CodeGroup>
    ```python Python icon="python" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    from prefab_ui.components import Button, Dialog, Row, Text
    from prefab_ui.actions import CloseOverlay
    from prefab_ui.actions.mcp import CallTool
    from prefab_ui.rx import Rx

    item_id = Rx("item_id")

    with Dialog(title="Delete Item", description="This action cannot be undone."):
        Button("Delete", variant="destructive")
        Text("Are you sure you want to permanently delete this item?")
        with Row(gap=2, css_class="justify-end"):
            Button("Cancel", variant="outline", on_click=CloseOverlay())
            Button(
                "Confirm Delete",
                variant="destructive",
                on_click=[
                    CallTool("delete_item", arguments={"id": item_id}),
                    CloseOverlay(),
                ],
            )
    ```

    ```json Protocol icon="brackets-curly" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    {
      "view": {
        "type": "Dialog",
        "title": "Delete Item",
        "description": "This action cannot be undone.",
        "dismissible": true,
        "children": [
          {
            "type": "Button",
            "label": "Delete",
            "variant": "destructive",
            "size": "default",
            "disabled": false
          },
          {
            "content": "Are you sure you want to permanently delete this item?",
            "type": "Text"
          },
          {
            "cssClass": "gap-2 justify-end",
            "type": "Row",
            "children": [
              {
                "type": "Button",
                "label": "Cancel",
                "variant": "outline",
                "size": "default",
                "disabled": false,
                "onClick": {"action": "closeOverlay"}
              },
              {
                "type": "Button",
                "label": "Confirm Delete",
                "variant": "destructive",
                "size": "default",
                "disabled": false,
                "onClick": [
                  {
                    "action": "toolCall",
                    "tool": "delete_item",
                    "arguments": {"id": "{{ item_id }}"}
                  },
                  {"action": "closeOverlay"}
                ]
              }
            ]
          }
        ]
      }
    }
    ```
  </CodeGroup>
</ComponentPreview>

## Confirmation with Feedback

Wire `ShowToast` callbacks onto a `CallTool` to give feedback after the server responds. `CloseOverlay()` in the `on_success` list dismisses the dialog once the operation succeeds; `on_error` keeps the dialog open so the user can retry.

```python theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
from prefab_ui.components import Button, Dialog, Row, Text
from prefab_ui.actions import CloseOverlay, ShowToast
from prefab_ui.actions.mcp import CallTool
from prefab_ui.rx import Rx

user_name = Rx("user_name")
user_id = Rx("user_id")

with Dialog(title="Remove User", description="This will revoke all access."):
    Button("Remove", variant="destructive")
    Text(f"User {user_name} will lose access to all projects.")
    with Row(gap=2, css_class="justify-end"):
        Button("Cancel", variant="outline", on_click=CloseOverlay())
        Button(
            "Remove User",
            variant="destructive",
            on_click=CallTool(
                "remove_user",
                arguments={"user_id": user_id},
                on_success=[
                    ShowToast("User removed", variant="success"),
                    CloseOverlay(),
                ],
                on_error=ShowToast("{{ $error }}", variant="error"),
            ),
        )
```

## State-Controlled Dialog

By default, dialogs open when the user clicks the trigger. Set `name` to bind the open state to a state variable, which lets you open the dialog programmatically via `SetState`. Both the trigger button and the external button open the same dialog:

<ComponentPreview height="400px" json={{"view":{"cssClass":"gap-3","type":"Column","children":[{"type":"Dialog","title":"Help","name":"show_help","dismissible":true,"children":[{"type":"Button","label":"Open via trigger","variant":"outline","size":"default","disabled":false},{"content":"This dialog can be opened by clicking the trigger or by any action that sets show_help to true.","type":"Text"}]},{"type":"Button","label":"Open via SetState","variant":"secondary","size":"default","disabled":false,"onClick":{"action":"setState","key":"show_help","value":true}}]}}} playground="ZnJvbSBwcmVmYWJfdWkuY29tcG9uZW50cyBpbXBvcnQgQnV0dG9uLCBDb2x1bW4sIERpYWxvZywgUm93LCBUZXh0CmZyb20gcHJlZmFiX3VpLmFjdGlvbnMgaW1wb3J0IFNldFN0YXRlCgp3aXRoIENvbHVtbihnYXA9Myk6CiAgICB3aXRoIERpYWxvZyh0aXRsZT0iSGVscCIsIG5hbWU9InNob3dfaGVscCIpOgogICAgICAgIEJ1dHRvbigiT3BlbiB2aWEgdHJpZ2dlciIsIHZhcmlhbnQ9Im91dGxpbmUiKQogICAgICAgIFRleHQoIlRoaXMgZGlhbG9nIGNhbiBiZSBvcGVuZWQgYnkgY2xpY2tpbmcgdGhlIHRyaWdnZXIgb3IgYnkgYW55IGFjdGlvbiB0aGF0IHNldHMgc2hvd19oZWxwIHRvIHRydWUuIikKICAgIEJ1dHRvbigKICAgICAgICAiT3BlbiB2aWEgU2V0U3RhdGUiLAogICAgICAgIHZhcmlhbnQ9InNlY29uZGFyeSIsCiAgICAgICAgb25fY2xpY2s9U2V0U3RhdGUoInNob3dfaGVscCIsIFRydWUpLAogICAgKQo">
  <CodeGroup>
    ```python icon="python" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    from prefab_ui.components import Button, Column, Dialog, Row, Text
    from prefab_ui.actions import SetState

    with Column(gap=3):
        with Dialog(title="Help", name="show_help"):
            Button("Open via trigger", variant="outline")
            Text("This dialog can be opened by clicking the trigger or by any action that sets show_help to true.")
        Button(
            "Open via SetState",
            variant="secondary",
            on_click=SetState("show_help", True),
        )
    ```

    ```json Protocol icon="brackets-curly" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    {
      "view": {
        "cssClass": "gap-3",
        "type": "Column",
        "children": [
          {
            "type": "Dialog",
            "title": "Help",
            "name": "show_help",
            "dismissible": true,
            "children": [
              {
                "type": "Button",
                "label": "Open via trigger",
                "variant": "outline",
                "size": "default",
                "disabled": false
              },
              {
                "content": "This dialog can be opened by clicking the trigger or by any action that sets show_help to true.",
                "type": "Text"
              }
            ]
          },
          {
            "type": "Button",
            "label": "Open via SetState",
            "variant": "secondary",
            "size": "default",
            "disabled": false,
            "onClick": {"action": "setState", "key": "show_help", "value": true}
          }
        ]
      }
    }
    ```
  </CodeGroup>
</ComponentPreview>

When `name` is set, `SetState("show_help", True)` opens the dialog and `SetState("show_help", False)` closes it. The trigger button still works too. Closing the dialog (via the X button, Escape, or clicking outside) writes `False` back to state.

This is the mechanism behind [keyboard shortcuts](/reference/app#keyboard-shortcuts): bind a key to `SetState(name, True)` to open any dialog from the keyboard.

## Non-Dismissible Dialog

Set `dismissible=False` when the user must make an explicit choice. Outside clicks, Escape, and the X button are all disabled. The only way to close the dialog is through a `CloseOverlay()` action on a button you provide:

<ComponentPreview height="400px" json={{"view":{"type":"Dialog","title":"Terms of Service","description":"You must accept to continue.","dismissible":false,"children":[{"type":"Button","label":"Review Terms","variant":"default","size":"default","disabled":false},{"content":"By clicking Accept, you agree to the Galactic Terms of Service.","type":"Text"},{"cssClass":"gap-2 justify-end","type":"Row","children":[{"type":"Button","label":"Accept","variant":"default","size":"default","disabled":false,"onClick":{"action":"closeOverlay"}}]}]}}} playground="ZnJvbSBwcmVmYWJfdWkuY29tcG9uZW50cyBpbXBvcnQgQnV0dG9uLCBDb2x1bW4sIERpYWxvZywgUm93LCBUZXh0CmZyb20gcHJlZmFiX3VpLmFjdGlvbnMgaW1wb3J0IENsb3NlT3ZlcmxheQoKd2l0aCBEaWFsb2coCiAgICB0aXRsZT0iVGVybXMgb2YgU2VydmljZSIsCiAgICBkZXNjcmlwdGlvbj0iWW91IG11c3QgYWNjZXB0IHRvIGNvbnRpbnVlLiIsCiAgICBkaXNtaXNzaWJsZT1GYWxzZSwKKToKICAgIEJ1dHRvbigiUmV2aWV3IFRlcm1zIikKICAgIFRleHQoIkJ5IGNsaWNraW5nIEFjY2VwdCwgeW91IGFncmVlIHRvIHRoZSBHYWxhY3RpYyBUZXJtcyBvZiBTZXJ2aWNlLiIpCiAgICB3aXRoIFJvdyhnYXA9MiwgY3NzX2NsYXNzPSJqdXN0aWZ5LWVuZCIpOgogICAgICAgIEJ1dHRvbigiQWNjZXB0Iiwgb25fY2xpY2s9Q2xvc2VPdmVybGF5KCkpCg">
  <CodeGroup>
    ```python icon="python" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    from prefab_ui.components import Button, Column, Dialog, Row, Text
    from prefab_ui.actions import CloseOverlay

    with Dialog(
        title="Terms of Service",
        description="You must accept to continue.",
        dismissible=False,
    ):
        Button("Review Terms")
        Text("By clicking Accept, you agree to the Galactic Terms of Service.")
        with Row(gap=2, css_class="justify-end"):
            Button("Accept", on_click=CloseOverlay())
    ```

    ```json Protocol icon="brackets-curly" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    {
      "view": {
        "type": "Dialog",
        "title": "Terms of Service",
        "description": "You must accept to continue.",
        "dismissible": false,
        "children": [
          {
            "type": "Button",
            "label": "Review Terms",
            "variant": "default",
            "size": "default",
            "disabled": false
          },
          {
            "content": "By clicking Accept, you agree to the Galactic Terms of Service.",
            "type": "Text"
          },
          {
            "cssClass": "gap-2 justify-end",
            "type": "Row",
            "children": [
              {
                "type": "Button",
                "label": "Accept",
                "variant": "default",
                "size": "default",
                "disabled": false,
                "onClick": {"action": "closeOverlay"}
              }
            ]
          }
        ]
      }
    }
    ```
  </CodeGroup>
</ComponentPreview>

## API Reference

<Card icon="code" title="Dialog Parameters">
  <ParamField body="title" type="str | None" default="None">
    Dialog header title.
  </ParamField>

  <ParamField body="description" type="str | None" default="None">
    Dialog header description text.
  </ParamField>

  <ParamField body="name" type="str | None" default="None">
    State key to bind open/close state. When set, the dialog can be opened programmatically via `SetState(name, True)`. No initial state setup needed; the dialog defaults to closed.
  </ParamField>

  <ParamField body="dismissible" type="bool" default="True">
    Whether the dialog can be closed by clicking outside, pressing Escape, or the X button. When `False`, the user must use an explicit `CloseOverlay()` action.
  </ParamField>

  <ParamField body="css_class" type="str | None" default="None">
    Additional Tailwind CSS classes.
  </ParamField>
</Card>

## Protocol Reference

```json Dialog theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
{
  "type": "Dialog",
  "id?": "string",
  "onMount?": "any",
  "children?": "[Component]",
  "let?": "object",
  "title?": "string",
  "description?": "string",
  "name?": "string",
  "dismissible?": true,
  "cssClass?": "string"
}
```

For the complete protocol schema, see [Dialog](/protocol/dialog).
