Multi-chain Swap
beui/swapFreeBlock
Cross-chain swap widget with chain + token selectors, morphing views, animated flip and quote.
Install it
npx shadcn@latest add https://beui.dev/r/swap.jsonSource
1"use client";2// beui.dev/components/blocks/swap34import { useEffect, useMemo, useState } from "react";5import { Settings } from "lucide-react";6import { useReducedMotion } from "motion/react";7import { cn } from "@/lib/utils";8import { CHAINS, TOKENS } from "./swap/data";9import { ActionButton, DestinationRow, FlipButton } from "./swap/controls";10import { Field } from "./swap/field";11import { QuoteRow } from "./swap/quote-row";12import { TokenPicker } from "./swap/token-picker";13import type { Chain, Token, TokenSide } from "./swap/types";14import { formatAmount } from "./swap/utils";1516export type { Chain, Token } from "./swap/types";17export { SWAP_DRAWER_EASE } from "./swap/constants";1819export interface MultiChainSwapProps {20 chains?: Chain[];21 tokens?: Token[];22 defaultFromId?: string;23 defaultToId?: string;24 className?: string;25}2627export function MultiChainSwap({28 chains = CHAINS,29 tokens = TOKENS,30 defaultFromId = "eth-eth",31 defaultToId = "sol-sol",32 className,33}: MultiChainSwapProps) {34 const reduce = useReducedMotion();35 const [fromId, setFromId] = useState(defaultFromId);36 const [toId, setToId] = useState(defaultToId);37 const [amount, setAmount] = useState("1");38 const [flipRot, setFlipRot] = useState(0);39 const [quoting, setQuoting] = useState(false);40 const [picking, setPicking] = useState<TokenSide | null>(null);41 const [showDest, setShowDest] = useState(false);42 const [destAddress, setDestAddress] = useState("");4344 const from = findToken(tokens, fromId);45 const to = findToken(tokens, toId);46 const fromChain = findChain(chains, from.chainId);47 const toChain = findChain(chains, to.chainId);4849 const numericAmount = Number(amount) || 0;50 const quoteKey = `${numericAmount}:${fromId}:${toId}`;51 const rate = useMemo(() => {52 if (!from.usd || !to.usd) return 1;53 return from.usd / to.usd;54 }, [from.usd, to.usd]);55 const toAmount = numericAmount * rate;5657 useEffect(() => {58 if (!quoteKey) return;59 if (numericAmount === 0) return;60 setQuoting(true);61 const id = setTimeout(() => setQuoting(false), 450);62 return () => clearTimeout(id);63 }, [numericAmount, quoteKey]);6465 const flip = () => {66 setFlipRot((r) => r + 180);67 setFromId(toId);68 setToId(fromId);69 };7071 const pickToken = (id: string) => {72 if (!picking) return;7374 if (picking === "from") {75 if (id === toId) setToId(fromId);76 setFromId(id);77 } else {78 if (id === fromId) setFromId(toId);79 setToId(id);80 }8182// … truncated
What it pulls in
npm packages
Files it writes
More from beui
All 104 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| File Upload Attachment Uploadattachment-upload | beui | Blocks | Free | 4 deps · 5 files | |
| Availability Scheduleravailability-scheduler | beui | Blocks | Free | 4 deps · 15 files | |
| Bloom Menubloom-menu | beui | Blocks | Free | 4 deps · 3 files | |
| Command Palettecommand-palette | beui | Blocks | Free | 4 deps · 3 files | |
| Dynamic Islanddynamic-island | beui | Blocks | Free | 3 deps · 3 files | |
| Expandable Action Barexpandable-action-bar | beui | Blocks | Free | 3 deps · 2 files | |
| Expandable Tabsexpandable-tabs | beui | Blocks | Free | 3 deps · 3 files | |
| Feedback Widgetfeedback-widget | beui | Blocks | Free | 4 deps · 9 files |
