Skip to main content
To render the same flow in your layout instead of a dialog, use CalmEmbed.

Import

Usage

app/page.tsx

Props

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.
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.
app/page.tsx
Cash App and the exchange onramp are available but not yet recommended for production.

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:
Picker with Crypto and Cash tabs, crypto tab active, showing Deposit Crypto, Swap to USDC and Deposit from Coinbase

Both sections configured, crypto tab active

Picker with the cash tab active, showing Deposit with Card, Deposit via ACH and Deposit with Cash App

The same config on the cash tab

A single-section config renders a plain list — no tab bar:
Picker with a single Deposit with Card row and no tabs

A cash-only config with just the card method

Picker with a single Deposit Crypto row and no tabs

A crypto-only config with just the blink method

The flat array

A flat Flow[] still works: the picker files each method into its section for you, keeping your order within each section.
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.
app/page.tsx
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".
app/page.tsx

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. 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.
app/page.tsx
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 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:
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:

SSR

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