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

# CalmDialog

> The single-component modal that wraps the onramp.

To render the same flow in your layout instead of a dialog, use
[CalmEmbed](/sdk/react/CalmEmbed).

## Import

```ts theme={null}
import { CalmDialog } from "@calm-xyz/react";
```

## Usage

```tsx app/page.tsx theme={null}
"use client";
import { CalmDialog } from "@calm-xyz/react";

export function FundButton() {
  return (
    <CalmDialog>
      <button>Deposit</button>
    </CalmDialog>
  );
}
```

## Props

```ts theme={null}
import type { CalmDialogProps } from "@calm-xyz/react";
```

### `children`

`React.ReactNode`

The element that opens the modal. Rendered inline as the trigger;
clicking it opens the dialog. Typically a `<button>`, but any element
that accepts `onClick` works.

### `flows`

`FlowConfig` — optional

The deposit methods to offer, listed in the order you want them
rendered within each section. Group them into the picker's two sections,
or pass a flat array and the picker groups it for you. Every method you
list renders as a row — the config is authoritative, so list only the
methods that work for your chain and currency.

```ts theme={null}
type FlowConfig = { cash?: CashFlow[]; crypto?: CryptoFlow[] } | Flow[];

type CashFlow = "bank" | "cashapp" | "card" | "card[stripe]";

type CryptoFlow = "swap" | "exchange" | "blink";
```

| Flow           | Section | Method                                                                 |
| -------------- | ------- | ---------------------------------------------------------------------- |
| `swap`         | crypto  | Swap an existing token balance to the destination chain's payout token |
| `exchange`     | crypto  | Transfer from a crypto exchange                                        |
| `blink`        | crypto  | Deposit Crypto                                                         |
| `bank`         | cash    | Bank transfer — ACH/wire (USD), SEPA (EUR), Faster Payments (GBP)      |
| `cashapp`      | cash    | Cash App (USD only)                                                    |
| `card`         | cash    | Debit or credit card                                                   |
| `card[stripe]` | cash    | Debit or credit card via Stripe                                        |

Defaults to `["swap", "bank"]`. List `cashapp`, `exchange`, or a card
provider explicitly to add them. List at most one card provider across
the whole config — activating both `card` and `card[stripe]` throws an
error. `cashapp` is USD-only and throws when rendered with any
other `currency`.

```tsx app/page.tsx theme={null}
<CalmDialog
  flows={{
    crypto: ["blink", "swap"],
    cash: ["card[stripe]", "bank"],
  }}
>
  <button>Deposit</button>
</CalmDialog>
```

<Warning>
  Cash App and the exchange onramp are available but not yet recommended
  for production.
</Warning>

#### Sections

The methods render behind two tabs, crypto first, opening on crypto.
The bar appears only when both sections hold at least one method, so
configuring a single section gives you a plain list.

Each array may only hold methods from its own section. Putting `blink`
under `cash` throws.

#### What each shape renders

A full config draws the two tabs with every method you listed:

```tsx theme={null}
<CalmDialog
  flows={{
    cash: ["card[stripe]", "bank", "cashapp"],
    crypto: ["blink", "swap", "exchange"],
  }}
>
  <button>Deposit</button>
</CalmDialog>
```

<Frame caption="Both sections configured, crypto tab active">
  <img src="https://mintcdn.com/calm/uUKO2ocvYsisgUpY/images/onramp/config-all-crypto-tab.png?fit=max&auto=format&n=uUKO2ocvYsisgUpY&q=85&s=5a36a58c1c552bde2cbcfa2783d9a520" alt="Picker with Crypto and Cash tabs, crypto tab active, showing Deposit Crypto, Swap to USDC and Deposit from Coinbase" width="704" height="692" data-path="images/onramp/config-all-crypto-tab.png" />
</Frame>

<Frame caption="The same config on the cash tab">
  <img src="https://mintcdn.com/calm/uUKO2ocvYsisgUpY/images/onramp/config-all-cash-tab.png?fit=max&auto=format&n=uUKO2ocvYsisgUpY&q=85&s=0d222272c4304bb963e441088e7f3c67" alt="Picker with the cash tab active, showing Deposit with Card, Deposit via ACH and Deposit with Cash App" width="704" height="692" data-path="images/onramp/config-all-cash-tab.png" />
</Frame>

A single-section config renders a plain list — no tab bar:

<Frame caption="A cash-only config with just the card method">
  <img src="https://mintcdn.com/calm/uUKO2ocvYsisgUpY/images/onramp/config-card-only.png?fit=max&auto=format&n=uUKO2ocvYsisgUpY&q=85&s=6e6fe42549acb58bf29af9b59336bba6" alt="Picker with a single Deposit with Card row and no tabs" width="704" height="296" data-path="images/onramp/config-card-only.png" />
</Frame>

<Frame caption="A crypto-only config with just the blink method">
  <img src="https://mintcdn.com/calm/uUKO2ocvYsisgUpY/images/onramp/config-crypto-only.png?fit=max&auto=format&n=uUKO2ocvYsisgUpY&q=85&s=74e76e200e6562f976117191e1b83c00" alt="Picker with a single Deposit Crypto row and no tabs" width="704" height="296" data-path="images/onramp/config-crypto-only.png" />
</Frame>

#### The flat array

A flat `Flow[]` still works: the picker files each method into its
section for you, keeping your order within each section.

```tsx theme={null}
<CalmDialog flows={["bank", "swap"]}>
  <button>Deposit</button>
</CalmDialog>
```

That is the same picker as `{ cash: ["bank"], crypto: ["swap"] }` — and
the same layout rule applies, so with both sections populated it draws
the two tabs. Reach for the grouped form in new integrations; it says
what renders where.

### `balance`

`number` — optional

The user's balance in your app, in the currency you configured. Shown
under the **Fund Wallet** title on the deposit picker, labelled with the
`name` you set on the provider — so a `name` of `Acme` reads
`Acme Balance: $12.34`.

```tsx app/page.tsx theme={null}
<CalmDialog balance={12.34}>
  <button>Deposit</button>
</CalmDialog>
```

You own this number. Calm renders what you pass and never reads it from
a wallet or an exchange, so a balance your app holds off-chain shows
exactly as your app knows it. It renders in the `locale` you set.

Leave the prop off and no line renders. Pass `0` to show a zero balance.

### `locale`

`string` — optional

The BCP-47 locale amounts render in. Separators and grouping follow the
locale's own conventions. Defaults to `"en-US"`.

```tsx app/page.tsx theme={null}
<CalmDialog locale="de-DE">
  <button>Deposit</button>
</CalmDialog>
```

## Connecting a wallet

A connected wallet is the prerequisite for the whole component. The
provider throws during render without one, so mount the tree only once
your wallet stack reports an authenticated user with a wallet.
Connecting happens in your app's own UI, before the modal exists.

From there the methods open on the first click.

### A cash-only integration

Cash deposits still settle USDC into a wallet, so a wallet still has to
exist before the modal can mount — what a cash-first product usually
wants is for sign-in to start from an email address rather than a
wallet prompt, and that is a setting on your wallet stack rather than a
prop here. Configure email as the first sign-in method and turn on
embedded wallets: your sign-in leads with email, the wallet gets created
behind the login, and the deposit methods appear without the user ever
handling one.

Both are settings on whatever produces the wallet, not on Calm. Set the
sign-in order so email leads, enable embedded wallet creation for users
who arrive without one, and enable each sign-in method you offer in that
product's own dashboard. Some stacks expose the ordering in code and some
only in their dashboard.

## The `blink` flow

Adds a **Deposit Crypto** row to the crypto section. Choosing it opens
Blink's deposit flow over your app; the user pays there, and the
destination chain's payout token arrives in the connected wallet.

```tsx app/page.tsx theme={null}
<CalmDialog flows={{ crypto: ["blink"], cash: ["bank"] }}>
  <button>Deposit</button>
</CalmDialog>
```

The row appears wherever you list it. This rail delivers to every
supported destination chain — Ethereum, Base, HyperEVM, HyperCore,
MegaETH, Monad, Polygon, and Arbitrum.

The deposit flow renders light or dark to match your modal. It follows
the modal surface you set with `--calm-popover`, so a dark override in
[Styling](/sdk/react/styling) carries into it with no extra configuration.

### If your app sets a Content-Security-Policy

The deposit flow renders in an iframe, so `https://pay.blink.cash` has
to be in your `frame-src` allowlist. Add it to the directive you
already have rather than replacing it:

```
frame-src https://pay.blink.cash <your existing origins>;
```

Other flows mount iframes of their own, so any origin already in your
`frame-src` must stay there. Dropping one blocks that flow the same way
a missing entry blocks this one: the browser refuses the frame and the
deposit never starts.

Outside production the flow is served from a different origin, so
allowlist the one your environment is pointed at as well as the
production host.

The flow measures the viewport to size itself, reading the viewport
height and the bottom safe-area inset, and switching to a sheet layout
below a width threshold. Without the standard viewport meta tag a phone
reports a desktop-sized virtual viewport, so the flow measures the
wrong one and stays in the desktop layout:

```html theme={null}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
```

## SSR

`<CalmDialog>` is a client component. In Next.js App Router, render
it from a parent that has `"use client"` at the top:

```tsx theme={null}
"use client";
import { CalmDialog } from "@calm-xyz/react";
```
