ZAYX logo
XGitHubDiscordReddit

Currency Switcher

A fluid fintech currency switcher with live exchange rates, bidirectional inputs, and spring-driven interactions.

Open in Github
Open in ChatGPT
Open in Claude
Open in Cursor

Bidirectional amount input

Edit either the send or receive field; the opposite amount updates live from the current exchange rate.

Spring-physics card swap

From/to currencies animate past each other with a double-tap guard so rapid taps never corrupt state.

Live exchange rates

Fetches from the Frankfurter API out of the box, with a 15-minute in-memory cache and stale-request cancellation. Swap in any backend via an injectable fetcher prop.

Morph-transition picker

The currency dropdown spring-scales from its pill anchor on open and collapses back on dismiss. Searchable by code or country name, with a double-select guard.

Installation

Copy the command directly into your project.

npx degit ManasCodeXart/ZAYX/components/currency-switch components/currency-switch

Install the needed dependencies

npx expo install react-native-reanimated react-native-worklets react-native-svg

Use in your app

Built to match the preview exactly — for the complete implementation, check the repo on GitHub.

import { StyleSheet, View } from "react-native"; import { CurrencySwitcher } from "../../components/currency-switch/CurrencySwitcher"; export default function HomeScreen() { return ( <View style={styles.container}> <CurrencySwitcher /> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#000", }, });

API

<CurrencySwitcher>

PropTypeDefaultDescription
currenciesreadonly Currency[]built-in listThe currency list shown in both pickers.
defaultFromCurrencyCurrencyUSDCurrency shown in the send card on first render.
defaultToCurrencyCurrencyINRCurrency shown in the receive card on first render.
defaultSendAmountstring"300"Amount pre-filled in the send field on first render.
ratenumberPass an explicit rate to skip the internal Frankfurter fetch entirely.
isRateLoadingbooleanWhen using an external rate, set this while your rate loads to show the Fetching… state.
feenumber76.87Transaction fee displayed in the info section.
arrivalEstimatestring"By Friday"Arrival estimate string displayed in the info section.
swapIconReactNodebuilt-in SwapIconReplace the swap button icon with any node.
styleStyleProp<ViewStyle>Additional styles applied to the root container.
onAmountChange(amount: string) => voidFires on every keystroke with the send-side amount as a string.
onCurrencyChange(from: Currency, to: Currency) => voidFires when either currency is changed or the cards are swapped.
onSend(details: { amount: number; from: Currency; to: Currency }) => voidFires on Send tap with the current send amount and both currencies.

<CurrencyPicker>

PropTypeDefaultDescription
visiblebooleanControls open/close. Morph-animates in and out automatically.
anchorPositionAnchorPosition | nullScreen position of the pill that triggered the picker, used to anchor the dropdown.
selectedCodestringThe currently selected currency code. Highlighted in the list.
currenciesreadonly Currency[]The full list to render and filter.
onSelect(currency: Currency) => voidFired once per selection; double-select is guarded internally.
onClose() => voidFired on backdrop tap or after a selection.

useExchangeRate

const { rate, isLoading, error, refresh } = useExchangeRate(base, target, options);
OptionTypeDefaultDescription
fetcherExchangeRateFetcherFrankfurterAny (base, target) => Promise<number> function.
cacheTtlMsnumber900000 (15 min)How long a cached rate is considered fresh before refetch.
enabledbooleantrueSet to false to skip fetching entirely — used internally when the rate prop is provided.

Types

interface Currency { readonly code: string; readonly symbol: string; readonly name: string; readonly countryCode: string; // ISO 3166-1 alpha-2, used for flag CDN lookup } type ExchangeRateFetcher = (base: string, target: string) => Promise<number>; interface AnchorPosition { readonly x: number; readonly y: number; readonly width: number; readonly height: number; }

Project Structure

currency-switch/
├── components/
│ ├── CurrencySwitcher.tsx # Root component
│ ├── CurrencyPicker.tsx # Morph-transition currency dropdown
│ ├── CurrencyRow.tsx # Flag + code + name row
│ └── SwapIcon.tsx # Bidirectional arrow icon
├── constants/
│ ├── currencies.ts # Built-in currency list
│ ├── types.ts # Currency, AnchorPosition, ExchangeRateFetcher
│ └── scaling.ts # verticalScale helpers
├── hooks/
│ ├── useCardSwap.ts # Spring-physics card swap
│ ├── useExchangeRate.ts # Frankfurter fetch + cache
│ └── useMorphTransition.ts # Picker morph animation
├── providers/
│ └── frankfurter.ts # Default ExchangeRateFetcher implementation
└── utils/
├── amount.ts # Adaptive font sizing, amount formatting
├── currencies.ts # Currency list helpers
└── flag.ts # Flag CDN URL builder