Skip to main content
The Swap components provide a comprehensive interface for users to execute Token swaps.

Quick start

The Swap component can be used in two ways: as a complete pre-built component or as a customizable container. When you provide to and from props, it renders a complete swap interface including SwapAmountInput, SwapSettings, SwapToggleButton, SwapButton, and SwapToast. For more customization, use the children approach shown in the next example.
import { Swap } from '@coinbase/onchainkit/swap'; 
import type { Token } from '@coinbase/onchainkit/token';

const eth: Token = {
  name: 'ETH',
  address: '',
  symbol: 'ETH',
  decimals: 18,
  image:
    'https://wallet-api-production.s3.amazonaws.com/uploads/tokens/eth_288.png',
  chainId: 8453,
};

const usdc: Token = {
  name: 'USDC',
  address: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
  symbol: 'USDC',
  decimals: 6,
  image:
    'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/44/2b/442b80bd16af0c0d9b22e03a16753823fe826e5bfd457292b55fa0ba8c1ba213-ZWUzYjJmZGUtMDYxNy00NDcyLTg0NjQtMWI4OGEwYjBiODE2',
  chainId: 8453,
};

<Swap
  from={[eth]}
  to={[usdc]}
/>

Usage

Example using @coinbase/onchainkit/swap and @coinbase/onchainkit/wallet.
import { Avatar, Name } from '@coinbase/onchainkit/identity';
import { 
  Swap, 
  SwapAmountInput, 
  SwapToggleButton, 
  SwapButton, 
  SwapMessage, 
  SwapToast, 
} from '@coinbase/onchainkit/swap'; 
import { Wallet, ConnectWallet } from '@coinbase/onchainkit/wallet';
import { useAccount } from 'wagmi';
import type { Token } from '@coinbase/onchainkit/token';

export default function SwapComponents() {
  const { address } = useAccount();

  const ETHToken: Token = {
    address: "",
    chainId: 8453,
    decimals: 18,
    name: "Ethereum",
    symbol: "ETH",
    image: "https://dynamic-assets.coinbase.com/dbb4b4983bde81309ddab83eb598358eb44375b930b94687ebe38bc22e52c3b2125258ffb8477a5ef22e33d6bd72e32a506c391caa13af64c00e46613c3e5806/asset_icons/4113b082d21cc5fab17fc8f2d19fb996165bcce635e6900f7fc2d57c4ef33ae9.png",
  };

  const USDCToken: Token = {
    address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    chainId: 8453,
    decimals: 6,
    name: "USDC",
    symbol: "USDC",
    image: "https://dynamic-assets.coinbase.com/3c15df5e2ac7d4abbe9499ed9335041f00c620f28e8de2f93474a9f432058742cdf4674bd43f309e69778a26969372310135be97eb183d91c492154176d455b8/asset_icons/9d67b728b6c8f457717154b3a35f9ddc702eae7e76c4684ee39302c4d7fd0bb8.png",
  };

  // add other tokens here to display them as options in the swap
  const swappableTokens: Token[] = [ETHToken, USDCToken];


  return address ? (
    <Swap>
      <SwapAmountInput
        label="Sell"
        swappableTokens={swappableTokens}
        token={ETHToken}
        type="from"
      />
      <SwapToggleButton />
      <SwapAmountInput
        label="Buy"
        swappableTokens={swappableTokens}
        token={USDCToken}
        type="to"
      />
      <SwapButton />
      <SwapMessage />
      <SwapToast />
    </Swap>
  ) : (
    <Wallet>
      <ConnectWallet>
        <Avatar className="h-6 w-6" />
        <Name />
      </ConnectWallet>
    </Wallet>
  );
}
Note: This interface is for demonstration purposes only.The swap will execute and work out of the box when you implement the component in your own app.

Supported Swap Routers

The Swap component supports two swap routers: To use the 0x Aggregator, set the experimental.useAggregator prop to true. To sponsor swap transactions for your users, toggle the Paymaster using the isSponsored prop. By default, this will use the Coinbase Developer Platform Paymaster. You can configure sponsorship settings on the Paymaster page. For security reasons, we recommend setting up a contract allowlist in the Portal. Without a contract allowlist defined, your Paymaster will only be able to sponsor up to $1. The contract used in our Swap API is Uniswap’s Universal Router, which is deployed on Base at 0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD. Note that gas sponsorship will only work for Smart Wallets.
import { Avatar, Name } from '@coinbase/onchainkit/identity';
import {
  Swap,
  SwapAmountInput,
  SwapToggleButton,
  SwapButton,
  SwapMessage,
} from '@coinbase/onchainkit/swap';
import { Wallet, ConnectWallet } from '@coinbase/onchainkit/wallet';
import { useAccount } from 'wagmi';
import type { Token } from '@coinbase/onchainkit/token';

export default function SwapComponents() {
  const { address } = useAccount();

  const ETHToken: Token = {
    address: "",
    chainId: 8453,
    decimals: 18,
    name: "Ethereum",
    symbol: "ETH",
    image: "https://dynamic-assets.coinbase.com/dbb4b4983bde81309ddab83eb598358eb44375b930b94687ebe38bc22e52c3b2125258ffb8477a5ef22e33d6bd72e32a506c391caa13af64c00e46613c3e5806/asset_icons/4113b082d21cc5fab17fc8f2d19fb996165bcce635e6900f7fc2d57c4ef33ae9.png",
  };

  const USDCToken: Token = {
    address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    chainId: 8453,
    decimals: 6,
    name: "USDC",
    symbol: "USDC",
    image: "https://dynamic-assets.coinbase.com/3c15df5e2ac7d4abbe9499ed9335041f00c620f28e8de2f93474a9f432058742cdf4674bd43f309e69778a26969372310135be97eb183d91c492154176d455b8/asset_icons/9d67b728b6c8f457717154b3a35f9ddc702eae7e76c4684ee39302c4d7fd0bb8.png",
  };

return (
// ---cut-before---
// omitted for brevity

// Set isSponsored to true
<Swap isSponsored >
  ...
</Swap>
// ---cut-after---
);
}

Custom token pair

You can adjust to only allow swap between a token pair.
import { Avatar, Name } from '@coinbase/onchainkit/identity';
import {
  Swap,
  SwapAmountInput,
  SwapToggleButton,
  SwapButton,
  SwapMessage,
  SwapToast,
} from '@coinbase/onchainkit/swap';
import { Wallet, ConnectWallet } from '@coinbase/onchainkit/wallet';
import { useAccount } from 'wagmi';
import type { Token } from '@coinbase/onchainkit/token';

export default function SwapComponents() {
  const { address } = useAccount();

  const ETHToken: Token = {
    address: "",
    chainId: 8453,
    decimals: 18,
    name: "Ethereum",
    symbol: "ETH",
    image: "https://dynamic-assets.coinbase.com/dbb4b4983bde81309ddab83eb598358eb44375b930b94687ebe38bc22e52c3b2125258ffb8477a5ef22e33d6bd72e32a506c391caa13af64c00e46613c3e5806/asset_icons/4113b082d21cc5fab17fc8f2d19fb996165bcce635e6900f7fc2d57c4ef33ae9.png",
  };

  const USDCToken: Token = {
    address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    chainId: 8453,
    decimals: 6,
    name: "USDC",
    symbol: "USDC",
    image: "https://dynamic-assets.coinbase.com/3c15df5e2ac7d4abbe9499ed9335041f00c620f28e8de2f93474a9f432058742cdf4674bd43f309e69778a26969372310135be97eb183d91c492154176d455b8/asset_icons/9d67b728b6c8f457717154b3a35f9ddc702eae7e76c4684ee39302c4d7fd0bb8.png",
  };

return (
// ---cut-before---
// omitted for brevity

<Swap>
  <SwapAmountInput
    label="Sell"
    token={ETHToken}
    type="from"
  />
  <SwapToggleButton />
  <SwapAmountInput
    label="Buy"
    token={USDCToken}
    type="to"
  />
  <SwapButton />
  <SwapMessage />
  <SwapToast />
</Swap>
// ---cut-after---
);
}

Remove toggle button

You can remove SwapToggleButton to make swap unidirectional.
import { Avatar, Name } from '@coinbase/onchainkit/identity';
import {
  Swap,
  SwapAmountInput,
  SwapToggleButton,
  SwapButton,
  SwapMessage,
  SwapToast,
} from '@coinbase/onchainkit/swap';
import { Wallet, ConnectWallet } from '@coinbase/onchainkit/wallet';
import { useAccount } from 'wagmi';
import type { Token } from '@coinbase/onchainkit/token';

export default function SwapComponents() {
  const { address } = useAccount();

  const ETHToken: Token = {
    address: "",
    chainId: 8453,
    decimals: 18,
    name: "Ethereum",
    symbol: "ETH",
    image: "https://dynamic-assets.coinbase.com/dbb4b4983bde81309ddab83eb598358eb44375b930b94687ebe38bc22e52c3b2125258ffb8477a5ef22e33d6bd72e32a506c391caa13af64c00e46613c3e5806/asset_icons/4113b082d21cc5fab17fc8f2d19fb996165bcce635e6900f7fc2d57c4ef33ae9.png",
  };

  const USDCToken: Token = {
    address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    chainId: 8453,
    decimals: 6,
    name: "USDC",
    symbol: "USDC",
    image: "https://dynamic-assets.coinbase.com/3c15df5e2ac7d4abbe9499ed9335041f00c620f28e8de2f93474a9f432058742cdf4674bd43f309e69778a26969372310135be97eb183d91c492154176d455b8/asset_icons/9d67b728b6c8f457717154b3a35f9ddc702eae7e76c4684ee39302c4d7fd0bb8.png",
  };

return (
// ---cut-before---
// omitted for brevity

<Swap>
  <SwapAmountInput
    label="Sell"
    token={ETHToken}
    type="from"
  />
  <SwapAmountInput
    label="Buy"
    token={USDCToken}
    type="to"
  />
  <SwapButton />
  <SwapMessage />
  <SwapToast />
</Swap>
// ---cut-after---
);
}

Remove swap message

You can remove SwapMessage component.
import { Avatar, Name } from '@coinbase/onchainkit/identity';
import {
  Swap,
  SwapAmountInput,
  SwapToggleButton,
  SwapButton,
  SwapMessage,
  SwapToast,
} from '@coinbase/onchainkit/swap';
import { Wallet, ConnectWallet } from '@coinbase/onchainkit/wallet';
import { useAccount } from 'wagmi';
import type { Token } from '@coinbase/onchainkit/token';

export default function SwapComponents() {
  const { address } = useAccount();

  const ETHToken: Token = {
    address: "",
    chainId: 8453,
    decimals: 18,
    name: "Ethereum",
    symbol: "ETH",
    image: "https://dynamic-assets.coinbase.com/dbb4b4983bde81309ddab83eb598358eb44375b930b94687ebe38bc22e52c3b2125258ffb8477a5ef22e33d6bd72e32a506c391caa13af64c00e46613c3e5806/asset_icons/4113b082d21cc5fab17fc8f2d19fb996165bcce635e6900f7fc2d57c4ef33ae9.png",
  };

  const USDCToken: Token = {
    address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    chainId: 8453,
    decimals: 6,
    name: "USDC",
    symbol: "USDC",
    image: "https://dynamic-assets.coinbase.com/3c15df5e2ac7d4abbe9499ed9335041f00c620f28e8de2f93474a9f432058742cdf4674bd43f309e69778a26969372310135be97eb183d91c492154176d455b8/asset_icons/9d67b728b6c8f457717154b3a35f9ddc702eae7e76c4684ee39302c4d7fd0bb8.png",
  };

return (
// ---cut-before---
// omitted for brevity

<Swap>
  <SwapAmountInput
    label="Sell"
    token={ETHToken}
    type="from"
  />
  <SwapToggleButton />
  <SwapAmountInput
    label="Buy"
    token={USDCToken}
    type="to"
  />
  <SwapButton />
  <SwapToast />
</Swap>
// ---cut-after---
);
}

Override styles

You can override component styles using className.
import { Avatar, Name } from '@coinbase/onchainkit/identity';
import {
  Swap,
  SwapAmountInput,
  SwapToggleButton,
  SwapButton,
  SwapMessage,
  SwapToast,
} from '@coinbase/onchainkit/swap';
import { Wallet, ConnectWallet } from '@coinbase/onchainkit/wallet';
import { useAccount } from 'wagmi';
import type { Token } from '@coinbase/onchainkit/token';

export default function SwapComponents() {
  const { address } = useAccount();

  const ETHToken: Token = {
    address: "",
    chainId: 8453,
    decimals: 18,
    name: "Ethereum",
    symbol: "ETH",
    image: "https://dynamic-assets.coinbase.com/dbb4b4983bde81309ddab83eb598358eb44375b930b94687ebe38bc22e52c3b2125258ffb8477a5ef22e33d6bd72e32a506c391caa13af64c00e46613c3e5806/asset_icons/4113b082d21cc5fab17fc8f2d19fb996165bcce635e6900f7fc2d57c4ef33ae9.png",
  };

  const USDCToken: Token = {
    address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    chainId: 8453,
    decimals: 6,
    name: "USDC",
    symbol: "USDC",
    image: "https://dynamic-assets.coinbase.com/3c15df5e2ac7d4abbe9499ed9335041f00c620f28e8de2f93474a9f432058742cdf4674bd43f309e69778a26969372310135be97eb183d91c492154176d455b8/asset_icons/9d67b728b6c8f457717154b3a35f9ddc702eae7e76c4684ee39302c4d7fd0bb8.png",
  };

return (
// ---cut-before---
// omitted for brevity

<Swap>
  <SwapAmountInput
    label="Sell"
    token={ETHToken}
    type="from"
  />
  <SwapToggleButton className='border-[#EA580C]'/>
  <SwapAmountInput
    label="Buy"
    token={USDCToken}
    type="to"
  />
  <SwapButton className='bg-[#EA580C]'/>
  <SwapMessage />
  <SwapToast />
</Swap>
// ---cut-after---
);
}

Components

The components are designed to work together hierarchically. For each component, ensure the following:
  • <Swap /> - Set the user’s address and error handling.
  • <SwapAmountInput /> - Set the Token to swap and specify the input type (from or to).
  • <SwapToggleButton /> - Optional component to toggle between input types.
  • <SwapMessage /> - Optional component that displays a message related to the swap operation’s current state.
  • <SwapButton /> - Set the onSuccess and onError callbacks.
  • <SwapToast /> - Optional component to notify user of successful swap transaction.

Props

SwapProps

type SwapProps = {
  className?: string; // Optional className override for top div element
  config?: {
    maxSlippage: number; // Maximum acceptable slippage for a swap (e.g., 3 for 3%). This is a percentage, not basis points
  };
  experimental?: {
    useAggregator: boolean; // Whether to use a DEX aggregator. true - 0x Aggregator, false - Uniswap V3 (default: false)
  };
  isSponsored?: boolean; // An optional setting to sponsor swaps with a Paymaster. (default: false)
  onError?: (error: SwapError) => void; // An optional callback function that handles errors within the provider
  onStatus?: (lifecycleStatus: LifecycleStatus) => void; // An optional callback function that exposes the component lifecycle state
  onSuccess?: (transactionReceipt: TransactionReceipt) => void; // An optional callback function that exposes the transaction receipt
} & (
  | {
      children: ReactNode;
      to?: never;
      from?: never;
      disabled?: never;
      title?: never;
      headerLeftContent?: never;
    }
  | {
      children?: never;
      to?: Token[];
      from?: Token[];
      disabled?: boolean;
      title?: ReactNode; // Title for the Swap component. (default: "Swap")
      headerLeftContent?: ReactNode; // Header left content for the Swap component (eg. back button)
    }
);

SwapAmountInputProps

type SwapAmountInputProps = {
  className?: string; // Optional className override for top div element
  delayMs?: number; // The debounce delay in milliseconds
  label: string; // Descriptive label for the input field
  swappableTokens?: Token[]; // Swappable tokens
  token?: Token; // Selected token
  type: 'to' | 'from'; // Identifies if component is for toToken or fromToken
  render?: (props: {
    token: SwapUnit;
    setAmountToMax: () => void;
    onSetToken: (token: Token) => void;
    hasInsufficientBalance: boolean;
  }) => ReactNode; // Custom render function for complete control of amount input rendering
};

SwapButtonProps

type SwapButtonProps = {
  className?: string; // Optional className override for top div element
  disabled?: boolean; // Disables swap button
  label?: ReactNode; // Label for the swap button
  render?: (props: {
    onSubmit: () => void;
    isLoading: boolean;
    lifecycleStatus: LifecycleStatus;
    isDisabled: boolean;
    isSwapInvalid: boolean;
  }) => ReactNode; // Custom render function for complete control of button rendering
};

SwapMessageProps

type SwapMessageProps = {
  className?: string; // Optional className override for top div element
  render?: (props: { message: string | null }) => ReactNode; // Custom render function for complete control of message rendering
};

SwapToggleButtonProps

type SwapToggleButtonProps = {
  className?: string; // Optional className override for top div element
  render?: (props: { onToggle: () => void }) => ReactNode; // Custom render function for complete control of toggle button rendering
};

SwapToastProps

type SwapToastProps = {
  className?: string; // An optional CSS class name for styling the toast component
  duration?: number; // An optional value to customize time until toast disappears
  position?: 'top-center' | 'top-right' | 'bottom-center' | 'bottom-right'; // An optional position property to specify the toast's position on the screen
  render?: (props: {
    isToastVisible: boolean;
    transactionHash: string;
    resetToastState: () => void;
    chainExplorer: string;
  }) => ReactNode; // Custom render function for complete control of toast rendering
};

Supporting Types

type SwapError = {
  code: string; // The error code representing the type of swap error
  error: string; // The error message providing details about the swap error
  message: string; // The error message providing details about the swap error
};

type LifecycleStatus =
  | { statusName: 'init'; statusData: LifecycleStatusDataShared }
  | { statusName: 'error'; statusData: SwapError & LifecycleStatusDataShared }
  | { statusName: 'amountChange'; statusData: { amountFrom?: string; amountTo: string; tokenFrom?: Token; tokenTo?: Token } & LifecycleStatusDataShared }
  | { statusName: 'slippageChange'; statusData: LifecycleStatusDataShared }
  | { statusName: 'transactionPending'; statusData: LifecycleStatusDataShared }
  | { statusName: 'transactionApproved'; statusData: { transactionHash?: Hex; transactionType: SwapTransactionType } & LifecycleStatusDataShared }
  | { statusName: 'success'; statusData: { transactionReceipt: TransactionReceipt } & LifecycleStatusDataShared };
I