FX Send Receive

Helps an international payment arrive in full: it measures what the recipient actually gets against the invoice, after fees and bank deductions.

StopsHelps prevent an invoice being paid short after fees

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/fx-send-receive.json

The files land in your project. They're yours to edit.

Usage

fx-send-receive-usage.tsx
import { FxSendReceive, type FxAmountValue, type FxCharges, type FxReadback } from "@/components/ui/fx-send-receive"

<FxSendReceive
  from="USD"
  to="EUR"
  quote={QUOTE}
  expected={{ amount: 1200, currency: "EUR" }}
  recipientName="Lena"
  value={value}
  onValueChange={(v, r) => {
    setValue(v)
    setRead(r)
  }}
  charges={charges}
  onChargesChange={setCharges}
  locale="en-US"
/>

What it catches

  • Invoice amount typed into "You send" instead of "They receive"
  • Invoice in a different currency from the receiving side
  • SHA charges that intermediary banks may deduct on the way
  • Paying more than the invoice
  • Fixes: lock "They receive" at the exact amount, or switch charges to OUR

Props

FxSendReceive props
PropTypeDefault
from* (required)

Sending currency (ISO code).

string—
to* (required)

Receiving currency (ISO code).

string—
quote* (required)

Rate and fees from your provider. `rate` is receiving units per one sending unit.

FxQuote—
expected

What must arrive, e.g. the invoice. Turns on the wrong-box, short and over checks.

FxExpected | null—
expectedLabel

Word for what must arrive, e.g. "invoice" or "rent".

string—
recipientName

Recipient's name for the readback.

string—
value

Controlled amount and the side it was typed on.

FxAmountValue—
defaultValue

Initial amount when uncontrolled.

FxAmountValue{ side: "send", amount: null }
onValueChange

Called with the new value and the full readback (both amounts, fee, problems).

(value: FxAmountValue, readback: FxReadback) => void—
charges

Controlled SWIFT charge option. Leave all three charge props out to hide the switch.

FxCharges—
defaultCharges

Initial charge instruction when uncontrolled.

FxCharges—
onChargesChange

Called when the user picks OUR, SHA or BEN.

(charges: FxCharges) => void—
onReceiveCurrencyChange

Offer a one-tap switch of the receiving currency when the expected amount is in another one.

(currency: string) => void—
locale

BCP 47 locale for number and currency formatting.

string—
messages

Override any visible string.

Partial<FxSendReceiveMessages>—

* required. Also accepts the native props of <div> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Both amount fields have visible labels; the receive field gets aria-invalid when a warning applies
  • Charges choice is a radiogroup with roving tabindex and arrow-key navigation
  • Status line uses role=status; decorative icons are aria-hidden
  • Focus moves to the receive field after a fix that changes it
  • Animations are skipped under reduced motion

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

This is a UX guard rail. Enforce on the server: Re-quote the rate and fees on the server and charge against that server quote, with an idempotency key on the transfer.

  • Arithmetic on the quote you pass in; it does not fetch live rates or fees
  • Intermediary deductions are an estimate from your quote's maximum, not what banks will actually take
  • Does not check the recipient's account details or sanctions screening

What it stops

Helps prevent an invoice being paid short after fees

Also known as

wire transfer · swift · fx · currency converter · remittance · send money · our sha ben · exchange rate

Field type

Dependencies

Pairs well with