Skip to main content

Documentation Index

Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt

Use this file to discover all available pages before exploring further.

This guide shows our officially recommended pattern for building a wallet picker that surfaces every wallet a user can connect to (or install) through your dapp, in one ordered list. You’ll do the rendering and the click handling. The SDK does the merging, deduplication, and ordering. Prerequisites: A configured Dynamic client. Each connection option this guide covers requires its own client extension — add only what you intend to surface:
Connection option typeRequired client extension(s)
withWalletProvider (installed)Your chain extensions (addEvmExtension, addSolanaExtension, etc.)
walletConnect (URI deeplink/QR)addWalletConnect<Chain>Extension for each chain you want to surface
metamaskSdkUri (URI deeplink/QR)addMetaMaskUri<Chain>Extension for each chain you want to surface
inAppBrowserNone (just open the URL)
Install-only entriesNone
See Adding extensions for setup.

1. Fetch the catalogue

import { getWalletOptionsCatalogue } from '@dynamic-labs-sdk/client';

const walletOptions = await getWalletOptionsCatalogue({
  includeMobileOptions: true,
});
Each entry is a WalletOption with a name, iconUrl, an ordered connectionOptions array, and an optional installationUrls bag for wallets the user can install on this device. See the reference for the full shape. includeMobileOptions: true is what makes the catalogue include URI-based connection options (deeplinking and QR code), in-app browser entries, and install links alongside installed providers. Without it, you only get installed wallets.

2. Decide what to do when a wallet is clicked

Pick the first entry of the wallet’s connectionOptions array — the catalogue already orders it by priority (withWalletProvider > URI deeplink > inAppBrowser). Switch on its type to drive the right flow:
import { isMobile, type WalletConnectionOption, type WalletOption } from '@dynamic-labs-sdk/client';

async function onWalletClick(walletOption: WalletOption) {
  const [connectionOption] = walletOption.connectionOptions;

  // Install-only wallet — no live connection available
  if (!connectionOption) {
    return renderInstallationInstructions(walletOption);
  }

  switch (connectionOption.type) {
    case 'withWalletProvider':
      return connectInstalledWallet(connectionOption);
    case 'walletConnect':
    case 'metamaskSdkUri':
      return isMobile()
        ? openUriDeeplinkOnMobile(connectionOption)
        : showQrCodeOnDesktop(connectionOption);
    case 'inAppBrowser':
      return openInAppBrowser(connectionOption);
  }
}
The next four sections implement each of those handlers.

3. Connect installed wallets (withWalletProvider)

Requires the chain extension for each chain you support (addEvmExtension, addSolanaExtension, etc.). The catalogue has already resolved the right wallet provider key for the user’s device. Pass it straight into the connect-and-verify flow — Dynamic does the rest.
import { connectAndVerifyWithWalletProvider } from '@dynamic-labs-sdk/client';

async function connectInstalledWallet(option: WithWalletProviderConnectionOption) {
  await connectAndVerifyWithWalletProvider({
    walletProviderKey: option.walletProviderKey,
  });
}

Requires the matching extension(s):
  • walletConnectaddWalletConnect<Chain>Extension
  • metamaskSdkUriaddMetaMaskUri<Chain>Extension
Both URI-deeplink discriminators share the same shape — deeplinks: { native?, universal? } plus the type — but mint their URI through different SDK functions. Wrap the discrimination in one helper so the rest of your picker doesn’t care:
import {
  connectAndVerifyWithMetaMaskUriEvm,
  connectAndVerifyWithMetaMaskUriSolana,
} from '@dynamic-labs-sdk/client';
import {
  connectAndVerifyWithWalletConnectEvm,
} from '@dynamic-labs-sdk/evm/wallet-connect';
import {
  connectAndVerifyWithWalletConnectSolana,
} from '@dynamic-labs-sdk/solana/wallet-connect';

type UriDeeplinkOption = Extract<
  WalletConnectionOption,
  { type: 'walletConnect' | 'metamaskSdkUri' }
>;

async function mintConnectionUri(option: UriDeeplinkOption) {
  if (option.type === 'metamaskSdkUri') {
    return option.chain === 'EVM'
      ? connectAndVerifyWithMetaMaskUriEvm()
      : connectAndVerifyWithMetaMaskUriSolana();
  }
  return option.chain === 'EVM'
    ? connectAndVerifyWithWalletConnectEvm()
    : connectAndVerifyWithWalletConnectSolana();
}
Each function returns { uri, approval }:
  • uri is what the wallet needs to pair with your dapp.
  • approval is a promise that resolves once the user approves in their wallet.
UI tip: when you launch a URI deeplink or QR, instruct the user which app they should open or scan from. This is especially important for metamaskSdkUri: that URI is the MetaMask SDK’s own pairing format, not WalletConnect, so it only resolves inside MetaMask. The walletOption.name and walletOption.iconUrl from the catalogue make a good “Open in ” prompt. appendConnectionUriToDeeplink is generic across both discriminators — append the minted URI onto the wallet’s preferred deeplink and open it.
import { appendConnectionUriToDeeplink } from '@dynamic-labs-sdk/client';
import { getPreferredWalletDeepLink } from '@dynamic-labs-sdk/wallet-connect';

async function openUriDeeplinkOnMobile(option: UriDeeplinkOption) {
  const { uri, approval } = await mintConnectionUri(option);

  const baseDeeplink = getPreferredWalletDeepLink({ deeplinks: option.deeplinks });
  if (!baseDeeplink) return;

  window.open(
    appendConnectionUriToDeeplink({ connectionUri: uri, deeplinkUrl: baseDeeplink }),
    '_blank',
  );

  await approval();
}

Desktop: render the URI as a QR code

On desktop, encode the same URI into a QR code so the user can scan it from their mobile wallet:
import QRCode from 'qrcode';

async function showQrCodeOnDesktop(option: UriDeeplinkOption) {
  const { uri, approval } = await mintConnectionUri(option);

  const dataUrl = await QRCode.toDataURL(uri);
  // Render `dataUrl` in your modal / sheet however you usually render images.

  await approval();
}

5. Open in-app browsers (inAppBrowser)

No extension required. Mobile wallets that ship with their own in-app browser expose a URL template containing {{encodedDappURI}}. Substitute it with the encoded current URL and open the result — the wallet will load your dapp inside its own browser, where its injected provider is already available.
function openInAppBrowser(option: InAppBrowserConnectionOption) {
  const dappUrl = encodeURIComponent(window.location.href);
  const target = option.url.replace('{{encodedDappURI}}', dappUrl);
  window.open(target, '_blank');
}
UI tip: as with URI deeplinks, tell the user which wallet they’re being handed off to — the in-app browser URL takes them out of your dapp into the wallet’s own app, and a “Continue in ” label removes the surprise.
No extension required. When a wallet has no connectionOptions but has installationUrls, render an install affordance. The exact UI is yours — a button, a modal with platform-specific instructions, a pointer to the user’s app store, etc.:
import { isMobile, type WalletOption } from '@dynamic-labs-sdk/client';

function renderInstallationInstructions(walletOption: WalletOption) {
  const urls = walletOption.installationUrls;
  if (!urls) return;

  // Pick the right URL for the current platform.
  const target = isMobile()
    ? urls.ios ?? urls.android
    : urls.chrome ?? urls.firefox ?? urls.edge ?? urls.safari ?? urls.opera;

  // Render however fits your UI — this is just a placeholder.
  if (target) window.open(target, '_blank');
}

7. Group installed vs other (optional)

The catalogue’s default 'relevance' ordering already buckets installed wallets first. If you want to render them under separate headings, split by checking whether the first connection option is withWalletProvider:
const installed = walletOptions.filter(
  (wallet) => wallet.connectionOptions[0]?.type === 'withWalletProvider',
);
const others = walletOptions.filter(
  (wallet) => wallet.connectionOptions[0]?.type !== 'withWalletProvider',
);

See also