# Swap form

A React component in [Beamline](https://beamline.io/)'s [Trading](https://beamline.io/components/trading) category. Live demo: https://beamline.io/components/swap-form

Exchange one token for another: type what you pay (or Max), pick each token from a searchable list with balances, and the form quotes what you receive with the rate, price impact, minimum after slippage and fees; the quote refreshes as it ages, the two sides trade places with one press, Review shows exactly what will happen, and the swap reports its progress, result or reason.

## Use it for

- Swapping or converting between two assets at a quoted rate: a DEX, a wallet, a treasury converting currencies.
- Any 'you pay / you receive' exchange where the price is quoted, ages and can move before it is accepted.

## Not for

- Orders on an order book with limit, stop and size ([order-ticket](https://beamline.io/components/order-ticket)).
- Moving money between your own accounts at a fixed 1:1 (a form with [number-field](https://beamline.io/components/number-field)).
- Showing the market ([price-chart](https://beamline.io/components/price-chart), [order-book](https://beamline.io/components/order-book)).

## Anatomy

- root: a size container on the look's surface; a header with the title and the slippage setting
- pay panel: the amount (large, typed in the person's locale), its value in money, the token button (mark, symbol, chevron), the balance and Max
- flip: a round button between the panels; the two sides trade places
- receive panel: the quoted amount (a placeholder while quoting), its value, the token button
- rate line: 1 ETH = 3,412.20 USDC (press to read it the other way) and a hairline that drains as the quote ages, then refreshes
- details (fold): price impact (with a word: Low, High, Very high), minimum received after slippage, network fee, route
- action: a button that says what is missing (Enter an amount, Choose a token, Not enough ETH) until it can Review
- review: the same card grown in place: You pay, You receive at least, rate, impact, fee, Confirm swap and Back
- result: a drawn check and what was swapped with a link to the transaction, or the reason it failed with Try again
- token list: a search, the tokens with their balances and value, the chosen one checked; a sheet on a phone

## States

- empty (no amount)
- quoting (receive shows a placeholder, the action waits)
- quoted (rate ageing; refreshed when it runs out; a changed rate flashes)
- no route (the quote's refusal, said under the panels)
- not enough balance (the pay panel and the action say so)
- high price impact (orange, the word High; over 5% red, Very high)
- reviewing
- swapping (Confirm shows progress; nothing can be changed)
- swapped (result with the transaction link and New swap)
- failed (the reason, Try again)

## Keyboard

- Tab: amount, Max, pay token, flip, receive token, rate, details, action
- In the amount: digits and the locale's decimal sign; ↑/↓ do nothing (an amount is typed, not stepped)
- Token button opens the list with focus in its search; ↑/↓ move, Enter picks, Escape closes and returns focus
- Enter in the amount goes to Review when it is ready

## Motion

The flip button turns half a turn while the two tokens slide to each other's panel (useFlip) and the amounts cross-fade. A new quote's amount rolls in; a changed rate flashes up or down. The age line drains on the compositor (one transform) and only while a quote is on screen. Review grows the card in place to the summary and Back shrinks it; the result's check draws itself. Reduced motion: no turn or slide; the age line is still shown as a still bar that refreshes.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `tokens` (required) | `SwapToken[]` | — | { symbol, name, icon?, balance?, price? (in fiat), decimals? (display) }. |
| `from / defaultFrom / onFromChange` | `string` | — | The symbol paid. |
| `to / defaultTo / onToChange` | `string` | — | The symbol received. |
| `amount / defaultAmount / onAmountChange` | `string` | — | The typed amount, as text (so “0.” survives typing). |
| `slippage / defaultSlippage / onSlippageChange` | `number` | — | Fraction: 0.005 = 0.5%. Default 0.005. |
| `getQuote` (required) | `(request: SwapRequest, signal: AbortSignal) => Promise<SwapQuote>` | — | Called as the amount, tokens or slippage settle and when a quote ages out; an older request is aborted. A rejection's message is shown (No route…). |
| `onSwap` | `(request: SwapRequest, quote: SwapQuote) => Promise<{ href?: string } \| void>` | — | A rejection shows its message with Try again; an href becomes View transaction. |
| `quoteTtl` | `number` | — | Seconds a quote holds before it refreshes. Default 20. |
| `fiat` | `string` | — | The money symbol for values. Default "$". |
| `title` | `ReactNode` | — | Default “Swap”. |
| `className / classNames / style / ref` | `root` | — |  |

## Built from

- button
- popover
- [segmented-control](https://beamline.io/components/segmented-control)
- spinner

## Dependencies

`@radix-ui/react-popover`, `lucide-react`

## Import

```tsx
import { SwapForm } from "@/components/swap-form/swap-form";
```

## Get it

Swap form is part of Beamline: 200+ components and 50+ complete screens for React that your coding agent (Claude Code, Codex, Cursor or any MCP client) installs into your app through Beamline's MCP server, as a ready-built package or as plain React source you can change. $49 launch price, one payment, no subscription, lifetime updates, one licence for your whole team. [Get Beamline](https://beamline.io/checkout?pack=complete) · [Connect your agent](https://beamline.io/connect)

## More in Trading

- [Price chart](https://beamline.io/components/price-chart): Candles, line or area price chart with volume, OHLC legend and reference lines, built for live ticks.
- [Market heatmap](https://beamline.io/components/market-heatmap): Treemap of markets sized by weight and coloured by change, grouped by sector, with hatched no-data tiles.
- [Order book](https://beamline.io/components/order-book): Asks, spread and bids with cumulative depth bars, grouping, change flashes and keyboard price picking.
- [Options chain](https://beamline.io/components/options-chain): Calls and puts for one expiry around a centre column of strikes: bid, ask, implied volatility, delta, volume and open interest, the money shaded in, a…
- [Depth chart](https://beamline.io/components/depth-chart): Cumulative bid and ask depth as stepped areas around the mid, with hover readout.
- [Ticker tape](https://beamline.io/components/ticker-tape): A continuously scrolling row of symbols with price, change and mini trend; anyone can hold it: hover, keyboard focus or a tap.
- [Watchlist](https://beamline.io/components/watchlist): A selectable, sortable list of markets with mini trends, prices, changes and stars.
- [Trades tape](https://beamline.io/components/trades-tape): Recent trades, newest first, with side glyphs, large-print marking and one-time arrival flashes.
- [Order ticket](https://beamline.io/components/order-ticket): Buy/sell order entry with limit, market and stop types, % of balance, inline validation and a live cost summary.
- [Positions table](https://beamline.io/components/positions-table): Open positions with live unrealised PnL, liquidation warnings and close actions.
- [P&L calendar](https://beamline.io/components/pnl-calendar): A trader's month at a glance: every trading day's profit or loss in its cell, shaded deeper the bigger it was in the gain or loss colour and marked ▲ or…
- [Price ticker](https://beamline.io/components/price-ticker): Symbol, last price with a directional flash, 24h change and stats.
- [Funding countdown](https://beamline.io/components/funding-countdown): Perpetual funding rate with who pays, and a live countdown ring to the next funding.
