> ## 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.

# Connecting external wallets

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

## Prerequisites

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

## What you'll build

A "connect wallet" experience: show the wallet providers available in the user's browser, connect to the one they pick, and — when you need proof they own it — verify ownership. You'll build the provider list and the connect button, wired to the SDK's wallet functions.

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.

## Listing available providers

`getAvailableWalletProvidersData` returns the providers detected for the registered chains, each with a `key`, `groupKey`, and `metadata` (display name and icon).

<Tabs>
  <Tab title="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));

        button.addEventListener('click', () => connect(provider.key));
        container.append(button);
      }
    }

    async function connect(walletProviderKey: string) {
      // showConnected / showError are your implementation.
      try {
        const walletAccount = await connectAndVerifyWithWalletProvider({
          walletProviderKey,
        });
        showConnected(walletAccount);
      } catch (error) {
        showError(error);
      }
    }
    ```
  </Tab>

  <Tab title="React">
    ```tsx theme={"system"}
    import { useConnectAndVerifyWithWalletProvider } from '@dynamic-labs-sdk/react-hooks';
    import { useGetAvailableWalletProvidersData } from '@dynamic-labs-sdk/react-hooks';

    function WalletList() {
      const { data: providers = [] } = useGetAvailableWalletProvidersData();
      const { mutateAsync: connectAndVerify, isPending } =
        useConnectAndVerifyWithWalletProvider();

      return (
        <ul>
          {providers.map((provider) => (
            <li key={provider.key}>
              <button
                type="button"
                disabled={isPending}
                onClick={() =>
                  connectAndVerify({ walletProviderKey: provider.key })
                }
              >
                <img src={provider.metadata.icon} alt="" width={24} />
                {provider.metadata.displayName}
              </button>
            </li>
          ))}
        </ul>
      );
    }
    ```
  </Tab>
</Tabs>

<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.
</Note>

## Connect without verifying

When you only need to read an address or request signatures — not prove ownership — use `connectWithWalletProvider`. It returns the connected `WalletAccount` without asking the user to sign.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import { connectWithWalletProvider } from '@dynamic-labs-sdk/client';

    async function connectOnly(walletProviderKey: string) {
      const walletAccount = await connectWithWalletProvider({ walletProviderKey });
      // showAddress is your implementation.
      showAddress(walletAccount.address);
    }
    ```
  </Tab>

  <Tab title="React">
    ```tsx theme={"system"}
    import { useConnectWithWalletProvider } from '@dynamic-labs-sdk/react-hooks';

    function ConnectButton({ walletProviderKey }: { walletProviderKey: string }) {
      const { mutateAsync: connect, isPending, data: walletAccount } =
        useConnectWithWalletProvider();

      return (
        <div>
          <button
            type="button"
            disabled={isPending}
            onClick={() => connect({ walletProviderKey })}
          >
            {isPending ? 'Connecting…' : 'Connect'}
          </button>
          {walletAccount ? <p>Connected: {walletAccount.address}</p> : null}
        </div>
      );
    }
    ```
  </Tab>
</Tabs>

<Tip>
  Verifying a wallet that already belongs to a different account surfaces a conflict. Handle it with the flow in [Account conflict resolution](/javascript/building-ui/account-conflict-resolution).
</Tip>

## Handling errors

| Error / situation                       | When                                                 | What to do                                                                              |
| --------------------------------------- | ---------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `WalletAlreadyLinkedToAnotherUserError` | Verifying a wallet that's on another account         | See [Account conflict resolution](/javascript/building-ui/account-conflict-resolution). |
| `WalletScreeningBlockedError`           | Wallet blocked by compliance screening               | See [Handling blocked wallets](/javascript/building-ui/blocked-wallet-screening).       |
| `UserRejectedError`                     | User dismissed the wallet's connect/sign prompt      | Return the user to the list so they can retry or pick another wallet.                   |
| Empty provider list                     | No chain extension registered, or no wallet detected | Confirm a chain extension is registered; prompt the user to install a wallet.           |

## See also

* [Adding extensions](/javascript/reference/adding-extensions) — register the chains whose wallets you want to support
* [Connected wallets management](/javascript/building-ui/connected-wallets-management) — list, switch, and remove connected wallets
* [Account conflict resolution](/javascript/building-ui/account-conflict-resolution) — when a wallet belongs to another account
