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

# TapDialog

> Renders a modal dialog into the mount's own portal node, so it covers the surface without leaving the extension.

```ts theme={null}
function TapDialog(__namedParameters): 
  | ReactElement<unknown, string | JSXElementConstructor<any>>
  | null;
```

Renders a modal dialog into the mount's own portal node, so it covers the
surface without leaving the extension.

## Parameters

### \_\_namedParameters

[`TapDialogProps`](/extensions/reference/frontend-api/Interface.TapDialogProps)

## Returns

\| `ReactElement`\<`unknown`, `string` | `JSXElementConstructor`\<`any`>>
\| `null`

## Remarks

Rendered outside a Tappify mount it throws `TAP_OUTSIDE_HOST`, from the `useTap`
above it. Inside a mount whose shadow root has no portal node yet, opening it
throws `TapError` with code `TAP_NO_PORTAL`; `renderWithTap` provides that node
in tests. While open it traps Tab
inside itself, moves focus to the first focusable element and returns focus
where it was on close, and answers Escape and a click on the scrim with
`onClose`. The scrim follows `--tap-scrim` and the panel `--bg-raised`,
`--divider` and `--tap-radius`; the panel is at most 520 pixels wide. The
component keeps no open state of its own.

## Example

```tsx theme={null}
import { TapButton, TapDialog } from '@tappify/extension-sdk';
import { useState } from 'react';

function ExportDialog() {
  const [open, setOpen] = useState(false);
  return (
    <>
      <TapButton onClick={() => setOpen(true)}>Export</TapButton>
      <TapDialog
        open={open}
        title="Export keywords"
        onClose={() => setOpen(false)}
        footer={<TapButton onClick={() => setOpen(false)}>Done</TapButton>}
      >
        <span>The report covers the range in the filter bar.</span>
      </TapDialog>
    </>
  );
}
```
