SelectTokenModal
The popup token picker with search, common tokens, recent tokens, and keyboard navigation
SelectTokenModal is the popup token picker. Your users click a trigger (a button, an avatar, whatever you put inside it), the modal opens, they search and pick. That is the flow.
Tokens come from the TokenKit API. Anything launched on tokenkithq.io shows up here automatically, alongside the usual blue chips.
Basic Usage
Wrap your app in TokenKitWrapper once, then drop the modal in wherever you need a token picker.
1import { useState } from 'react';2import {3 TokenKitWrapper,4 SelectTokenModal,5 themes,6 type IToken,7} from 'starknet-tokenkit';8 9function App() {10 const [selectedToken, setSelectedToken] = useState<IToken | null>(null);11 12 return (13 <TokenKitWrapper14 network="SN_MAIN"15 apiKey="your-api-key"16 mainnetEndpoint="https://api.tokenkithq.io"17 sepoliaEndpoint="https://api.sepolia.tokenkithq.io"18 themeObject={themes.dark}19 options={{ enableRecent: true }}20 >21 <SelectTokenModal22 callBackFunc={(token) => setSelectedToken(token)}23 selectedToken={selectedToken}24 >25 <button>26 {selectedToken ? selectedToken.symbol : 'Select Token'}27 </button>28 </SelectTokenModal>29 </TokenKitWrapper>30 );31}There is no open / onClose. The native `
Props
| Prop | Type | Required | Description |
|---|---|---|---|
selectedToken | IToken | null | undefined | Yes | The currently selected token. Drives the "selected" highlight in the list. |
callBackFunc | (token: IToken) => void | Yes | Called with the chosen token when the user picks one. The modal closes automatically afterwards. |
children | ReactNode | No | The trigger element rendered before the dialog. Click anywhere on it to open. |
modalHeight | string | No | Modal height (e.g. "95dvh"). Defaults to the theme's height value. |
modalWidth | string | No | Modal width (e.g. "450px"). Defaults to 420px, shrinks on mobile. |
animation | 'bounce' | 'slide' | 'ease' | 'fade' | No | Open/close animation style. |
Wrapper-level options (TokenKitOptions)
Several picker behaviors are configured on TokenKitWrapper (or TokenKitProvider) via the options prop, not on SelectTokenModal itself — the modal reads them from context so every picker in the tree shares them.
1<TokenKitWrapper2 /* ...other props */3 options={{4 tokensToLoad: 'public', // or 'all' — see below5 enableRecent: true, // opt in to recent-tokens persistence6 }}7 origin="chrome-extension://abcdef..." // optional, for extension wallets8>| Option | Type | Default | What it does |
|---|---|---|---|
tokensToLoad | 'public' | 'all' | 'public' | 'public' shows only tokens flagged public=true in the TokenKit catalog (the curated set most apps want). 'all' shows every indexed ERC-20, including unverified contracts — use this when your users need to find arbitrary tokens by address. |
enableRecent | boolean | false | When true, recently-picked tokens are persisted to localStorage per-network and shown in a "Recent" section above the full list. When false, nothing is read from or written to storage. Opt in explicitly. |
origin (on the wrapper itself, not inside options) is sent as the X-Origin HTTP header on every API request — useful for browser-extension wallets to identify themselves to allow-listed API keys.
Features
- Search — by name, symbol, or contract address (debounced 400 ms)
- Common tokens — a horizontal quick-pick row at the top
- Recent tokens — opt in via
options.enableRecent; persisted tolocalStoragekeyed by network - Infinite scroll — more tokens load as you reach the bottom of the list
- Keyboard navigation:
↑/↓— move focus through resultsEnter— select the focused tokenEsc— close the modal
- Click-outside-to-close — clicking the backdrop closes the dialog
- Animated transitions — built-in fade/slide on open and close
- Selected-state styling — the currently selected token is dimmed and unclickable in the list
Modal and Container side by side
Sometimes you want both a popup picker and an always-visible list (say, the modal in your swap form and the container in a sidebar). They share the same selection state via React.
1import { useState } from 'react';2import {3 TokenKitWrapper,4 SelectTokenModal,5 SelectTokenContainer,6 themes,7 type IToken,8} from 'starknet-tokenkit';9 10function App() {11 const [selectedToken, setSelectedToken] = useState<IToken | null>(null);12 13 return (14 <TokenKitWrapper15 network="SN_MAIN"16 apiKey="your-api-key"17 mainnetEndpoint="https://api.tokenkithq.io"18 sepoliaEndpoint="https://api.sepolia.tokenkithq.io"19 themeObject={themes.dark}20 >21 {/* Modal — opens on trigger click */}22 <SelectTokenModal23 callBackFunc={setSelectedToken}24 selectedToken={selectedToken}25 modalHeight="95dvh"26 modalWidth="450px"27 >28 <button>Select Token (Modal)</button>29 </SelectTokenModal>30 31 {/* Container — renders inline */}32 <SelectTokenContainer33 callBackFunc={setSelectedToken}34 selectedToken={selectedToken}35 modalHeight="60dvh"36 modalWidth="400px"37 />38 </TokenKitWrapper>39 );40}The IToken shape
When the user picks a token, your callback gets an IToken:
1interface IToken {2 address: string; // Contract address3 name: string; // Token name (e.g. "Ethereum")4 symbol: string; // Token symbol (e.g. "ETH")5 decimals: number; // Token decimals (e.g. 18)6 logo: string; // URL to token logo image7 verified?: boolean; // Whether the token is verified8 public?: boolean; // Whether the token is publicly listed9 common?: boolean; // Whether the token is one of the curated common tokens10 id?: number; // TokenKit DB id (when present)11 price?: number | string | null; // Optional last-known USD price12}logo may be an empty string for some tokens — the picker falls back to initials in a circle, so you don't need to handle that case yourself.