> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Connecting external wallets

> Let users connect a browser-extension or mobile wallet — list the available wallets, connect, and verify ownership.

> [!TIP]
>
> Building a React web app? The prebuilt [connect wallet widget](https://docs.dynamic.xyz/docs/javascript/building-ui/react-ui/connect-wallet-widget.md) renders the wallet list, chain choice, QR pairing, and error screens for you.

## Prerequisites

Before this page: create and initialize a Dynamic client (see [Creating a Dynamic client](https://docs.dynamic.xyz/docs/javascript/reference/client/create-dynamic-client.md)), and register a chain extension so wallet providers are available (see [Adding extensions](https://docs.dynamic.xyz/docs/javascript/reference/adding-extensions.md)). External wallets only appear once a chain extension (EVM, Solana, etc.) is registered.

## What you'll build

A "connect wallet" experience: show the wallets a user can connect, connect to the one they pick, and — when you need proof they own it — verify ownership.

Two levels of connection:

* **Connect** — attach the wallet so you can read its address and prompt for signatures. Use this when you just need to interact with a wallet.
* **Connect and verify** — connect, then have the user sign a message proving ownership. Verifying links the wallet to the signed-in user (or signs them in). Use this when the wallet must be a trusted credential.

## Connect by wallet catalogue key

`connectWalletOption` is the primary way to connect an external wallet. It takes a wallet options catalogue key (such as `'metamask'` or `'phantom'`) and picks the connection path the wallet supports on the user's device: an installed wallet provider, a URI pairing (WalletConnect or the MetaMask SDK) handed to `onConnectionUri`, or the wallet's own in-app browser. You do not need to route by connection type yourself.

The function returns an unverified `WalletAccount`. Pass it to `verifyWalletAccount` to prove ownership and link the wallet to the user.

> [tab] TypeScript
>
> ```typescript theme={"system"}
> import {
>   connectWalletOption,
>   verifyWalletAccount,
> } from '@dynamic-labs-sdk/client';
>
> async function connect(walletKey: string) {
>   // showConnected / showError are your implementation.
>   try {
>     const walletAccount = await connectWalletOption({ walletKey });
>     const verifiedAccount = await verifyWalletAccount({ walletAccount });
>     showConnected(verifiedAccount);
>   } catch (error) {
>     showError(error);
>   }
> }
> ```

> [tab] React
>
> ```tsx theme={"system"}
> import {
>   useConnectWalletOption,
>   useVerifyWalletAccount,
> } from '@dynamic-labs-sdk/react-hooks';
>
> function ConnectButton({ walletKey }: { walletKey: string }) {
>   const { mutateAsync: connect, isPending: isConnecting } =
>     useConnectWalletOption();
>   const { mutateAsync: verify, isPending: isVerifying } =
>     useVerifyWalletAccount();
>
>   const onClick = async () => {
>     try {
>       const walletAccount = await connect({ walletKey });
>       await verify({ walletAccount });
>     } catch (error) {
>       // showError is your implementation.
>       showError(error);
>     }
>   };
>
>   return (
>     <button
>       type="button"
>       disabled={isConnecting || isVerifying}
>       onClick={onClick}
>     >
>       Connect
>     </button>
>   );
> }
> ```

Catalogue keys come from `getWalletOptionsCatalogue`, which lists every wallet a user can connect to or install — see [Build a wallet picker](https://docs.dynamic.xyz/docs/javascript/reference/wallets/build-wallet-picker.md) for the recommended picker pattern. For the full behavior, parameters, and errors, see [Connect a wallet by catalogue key](https://docs.dynamic.xyz/docs/javascript/reference/wallets/connect-wallet-option.md).

### Customizing the connection paths

Two optional handlers control how `connectWalletOption` reaches wallets that are not installed:

* `onConnectionUri` receives the pairing URI when the wallet connects through WalletConnect or the MetaMask SDK. It is required for wallets whose only connection path is a URI pairing — open the URI as a deeplink on mobile, or render it as a QR code on desktop.
* `handleWalletBrowserRedirect` is called when the wallet only provides its own in-app browser. Without it, the SDK redirects the current page into the wallet's browser itself; pass it to choose a different return page or when there is no current page URL.

```typescript theme={"system"}
import {
  connectWalletOption,
  isMobile,
  redirectToInAppBrowser,
  verifyWalletAccount,
} from '@dynamic-labs-sdk/client';

const walletAccount = await connectWalletOption({
  handleWalletBrowserRedirect: ({ chain, walletKey }) =>
    redirectToInAppBrowser({
      appUrl: window.location.href,
      chain,
      walletKey,
    }),
  onConnectionUri: ({ uri }) => {
    if (isMobile()) {
      window.location.href = uri;
      return;
    }

    renderQrCode(uri); // your implementation
  },
  walletKey: 'metamask',
});

// The connected account is unverified until you verify it.
await verifyWalletAccount({ walletAccount });
```

See [Connect a wallet by catalogue key](https://docs.dynamic.xyz/docs/javascript/reference/wallets/connect-wallet-option.md) for the parameters and the errors thrown when a required handler is missing.

### Reaching mobile and uninstalled wallets

`connectWalletOption` can only offer a URI pairing when the matching extension is registered — add them at app setup alongside your chain extensions:

* **WalletConnect (EVM and Solana)** — recommended for broad mobile wallet support. Add `addWalletConnectEvmExtension` from `@dynamic-labs-sdk/evm/wallet-connect` and `addWalletConnectSolanaExtension` from `@dynamic-labs-sdk/solana/wallet-connect`. See [Authenticate with WalletConnect](https://docs.dynamic.xyz/docs/javascript/reference/wallets/walletconnect-integration.md).
* **MetaMask SDK (EVM and Solana)** — recommended for MetaMask support, covering the desktop QR code and the mobile handoff. Add `addMetaMaskEvmExtension` from `@dynamic-labs-sdk/evm/metamask` and `addMetaMaskSolanaExtension` from `@dynamic-labs-sdk/solana/metamask`. See the [MetaMask Connection Guide](https://docs.dynamic.xyz/docs/javascript/reference/wallets/metamask-connection-guide.md).

Once an extension is registered, `connectWalletOption` routes to it automatically — WalletConnect and MetaMask SDK pairings arrive through the same `onConnectionUri` handler, so no extra wiring is needed beyond rendering or opening the URI.

## Listing installed providers

`getAvailableWalletProvidersData` returns only the providers detected as installed in the user's browser for the registered chains, each with a `key`, `groupKey`, and `metadata` (display name and icon). It does not include wallets reachable through URI pairing or an in-app browser, so prefer the catalogue-based flow above unless you specifically want an installed-extensions-only list.

> [tab] TypeScript
>
> ```typescript theme={"system"}
> import {
>   getAvailableWalletProvidersData,
>   connectAndVerifyWithWalletProvider,
> } from '@dynamic-labs-sdk/client';
>
> function renderWalletList(container: HTMLElement) {
>   const providers = getAvailableWalletProvidersData();
>
>   container.replaceChildren();
>   for (const provider of providers) {
>     const button = document.createElement('button');
>     button.type = 'button';
>
>     const icon = document.createElement('img');
>     icon.src = provider.metadata.icon;
>     icon.alt = '';
>     icon.width = 24;
>     button.append(icon, document.createTextNode(provider.metadata.displayName));
>
>     // provider.key is a wallet provider key, not a catalogue key.
>     button.addEventListener('click', () =>
>       connectAndVerifyWithWalletProvider({ walletProviderKey: provider.key }),
>     );
>     container.append(button);
>   }
> }
> ```

> [tab] React
>
> ```tsx theme={"system"}
> import {
>   useGetAvailableWalletProvidersData,
>   useConnectAndVerifyWithWalletProvider,
> } from '@dynamic-labs-sdk/react-hooks';
>
> function WalletList() {
>   const { data: providers = [] } = useGetAvailableWalletProvidersData();
>   const { mutate: connectAndVerify } =
>     useConnectAndVerifyWithWalletProvider();
>
>   return (
>     <ul>
>       {providers.map((provider) => (
>         <li key={provider.key}>
>           {/* provider.key is a wallet provider key, not a catalogue key. */}
>           <button
>             type="button"
>             onClick={() =>
>               connectAndVerify({ walletProviderKey: provider.key })
>             }
>           >
>             <img src={provider.metadata.icon} alt="" width={24} />
>             {provider.metadata.displayName}
>           </button>
>         </li>
>       ))}
>     </ul>
>   );
> }
> ```

> [!NOTE]
>
> Providers from different chains that represent the same wallet share a `groupKey`. Group your UI by `groupKey` if you want one row per wallet (for example, one "MetaMask" entry) instead of one row per chain.

### Connecting through an installed provider directly

`connectAndVerifyWithWalletProvider` and `connectWithWalletProvider` are the lower-level connection functions. They take a wallet provider key from `getAvailableWalletProvidersData` and drive that registered provider directly — for example, an EIP-6963 browser extension or a redirect provider such as Phantom on mobile. They cannot reach URI pairing or in-app browser wallets, so use them when you intentionally route by provider, such as when connecting a hardware wallet through a compatible software provider.

> [tab] TypeScript
>
> ```typescript theme={"system"}
> import {
>   connectAndVerifyWithWalletProvider,
>   connectWithWalletProvider,
> } from '@dynamic-labs-sdk/client';
>
> // Connect and verify in a single call.
> const walletAccount = await connectAndVerifyWithWalletProvider({
>   walletProviderKey,
> });
>
> // Or connect only, when you just need the address and signatures.
> const unverifiedAccount = await connectWithWalletProvider({
>   walletProviderKey,
> });
> ```

> [tab] React
>
> ```tsx theme={"system"}
> import {
>   useConnectAndVerifyWithWalletProvider,
>   useConnectWithWalletProvider,
> } from '@dynamic-labs-sdk/react-hooks';
>
> function ConnectButton({ walletProviderKey }: { walletProviderKey: string }) {
>   const { mutateAsync: connectAndVerify, isPending } =
>     useConnectAndVerifyWithWalletProvider();
>   const { mutateAsync: connectOnly } = useConnectWithWalletProvider();
>
>   return (
>     <button
>       type="button"
>       disabled={isPending}
>       onClick={() => connectAndVerify({ walletProviderKey })}
>     >
>       {isPending ? 'Connecting…' : 'Connect'}
>     </button>
>   );
> }
> ```

> [!TIP]
>
> Verifying a wallet that already belongs to a different account surfaces a conflict. Handle it with the flow in [Account conflict resolution](https://docs.dynamic.xyz/docs/javascript/building-ui/account-conflict-resolution.md).

## Handling errors

| Error / situation | When | What to do |
| - | - | - |
| `WalletAlreadyLinkedToAnotherUserError` | Verifying a wallet that's on another account | See [Account conflict resolution](https://docs.dynamic.xyz/docs/javascript/building-ui/account-conflict-resolution.md). |
| `WalletScreeningBlockedError` | Wallet blocked by compliance screening | Inform the user their wallet has been blocked by compliance screening. |
| `UserRejectedError` | User dismissed the wallet's connect/sign prompt | Return the user to the list so they can retry or pick another wallet. |
| `WalletOptionNotFoundError` | No wallet option matches the catalogue key | Check the key against `getWalletOptionsCatalogue`. |
| Empty provider list | No chain extension registered, or no wallet detected | Confirm a chain extension is registered; prompt the user to install a wallet. |

For the full `connectWalletOption` error list, see [Connect a wallet by catalogue key](https://docs.dynamic.xyz/docs/javascript/reference/wallets/connect-wallet-option.md#errors).

## See also

* [Connect a wallet by catalogue key](https://docs.dynamic.xyz/docs/javascript/reference/wallets/connect-wallet-option.md) — the primary connect API and its full reference
* [Connect wallet widget](https://docs.dynamic.xyz/docs/javascript/building-ui/react-ui/connect-wallet-widget.md) — a prebuilt connection UI for React web apps
* [Build a wallet picker](https://docs.dynamic.xyz/docs/javascript/reference/wallets/build-wallet-picker.md) — list every connectable wallet from the catalogue
* [Adding extensions](https://docs.dynamic.xyz/docs/javascript/reference/adding-extensions.md) — register the chains whose wallets you want to support
* Connected wallets management — list, switch, and remove connected wallets
* [Account conflict resolution](https://docs.dynamic.xyz/docs/javascript/building-ui/account-conflict-resolution.md) — when a wallet belongs to another account


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.