learn.sol
Solana Kit Clients • Wallet Standard Connection Flow
Lesson 3 of 11

Wallet Standard Connection Flow

Build a wallet connection panel that reflects the real session state, offers the right connectors, and never lies about connection status.

Wallet connection looks simple until the UI lies.

The usual bugs are not cryptographic.

They are product bugs.

A button says Connected before the wallet approves.

A rejected request looks like a crash.

A disconnected session still leaves send buttons enabled.

The fix is not more styling.

The fix is to treat wallet connection as explicit runtime state.

ConnectorTap to reveal
One Wallet Standard integration the runtime discovered in the browser, such as Phantom or another supported wallet.
Connector
Connection StatusTap to reveal
The runtime truth about whether a wallet session is absent, changing, connected, or failed.
Connection Status
Wallet SessionTap to reveal
The connected wallet account and related session data that signer-required actions depend on.
Wallet Session
Honest UITap to reveal
A wallet interface that only enables actions once the runtime actually says the session exists.
Honest UI

The Correct Mental Model

Your app does not own the wallet.

It asks the wallet connector for a session.

That session can be:

  • absent
  • connecting
  • connected
  • failed

Your UI should reflect that state exactly.

Nothing more.

Nothing less.

Step 1: Read Connection State From One Hook

The current React hooks docs expose useWalletConnection() as the main connection surface.

It gives you the available connectors, the current wallet session, the runtime status, and the connect/disconnect actions.

That is the right place to start.

Create components/solana/WalletPanel.tsx:

"use client";

import React from "react";
import { useWalletConnection } from "@solana/react-hooks";

function shortAddress(address?: string) {
  if (!address) return "";
  return `${address.slice(0, 4)}...${address.slice(-4)}`;
}

export function WalletPanel() {
  const { connectors, wallet, status, connect, disconnect } = useWalletConnection();

  const onConnect = async (connectorId: string) => {
    try {
      await connect({ connectorId });
    } catch (e) {
      console.error("connect failed", e);
    }
  };

  const onDisconnect = async () => {
    try {
      await disconnect();
    } catch (e) {
      console.error("disconnect failed", e);
    }
  };

  return (
    <div className="rounded-xl border border-zinc-700 p-4 space-y-3">
      <div className="text-sm text-zinc-300">Status: {status}</div>

      {wallet ? (
        <>
          <div className="text-sm text-zinc-200">
            Connected: {shortAddress(wallet.address)}
          </div>

          <button
            onClick={onDisconnect}
            className="px-3 py-2 rounded bg-red-600 text-white"
          >
            Disconnect
          </button>
        </>
      ) : connectors.length === 0 ? (
        <div className="text-sm text-zinc-300">No wallet connector was found in this browser.</div>
      ) : (
        <div className="space-y-2">
          <div className="text-sm text-zinc-300">Choose wallet:</div>

          {connectors.map((connector) => (
            <button
              key={connector.id}
              onClick={() => onConnect(connector.id)}
              className="mr-2 px-3 py-2 rounded bg-emerald-600 text-white"
            >
              Connect {connector.name}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

This component is doing only three honest things:

  • reading the current session state
  • listing the available wallet connectors
  • triggering connect or disconnect when the user asks

That is enough for a first wallet panel.

Read The Panel In The Right Order

connectors

These are the Wallet Standard connectors the client runtime discovered in the browser.

If there are no connectors, the right UI is not Connected.

The right UI is: there is no wallet available to choose.

status

This is the connection truth.

If the status is still changing, your UI should not pretend the wallet is ready.

That is why the status label matters.

wallet

This is the connected session.

If it exists, you can show the address and unlock signer-required actions.

If it does not, write buttons elsewhere in the app should stay disabled.

The Failure Modes To Avoid

Treating user rejection like an app error

A user can reject a wallet prompt.

That is normal.

Your UI should recover cleanly and stay usable.

Unlocking actions before the session exists

Do not assume a button click means a connected wallet.

Wait for the runtime state to say so.

Hiding which wallet the user is choosing

If there are multiple connectors, the user should see exactly which wallet they are selecting.

Ambiguity here creates avoidable trust problems.

What You Can Do After This

You can build a wallet panel that reflects real wallet-standard session state instead of guessing.

That gives the rest of the client a stable starting point.

The next lesson builds on that by reading chain state through the same shared runtime.

Quick Check

Quick Check
Single answer

Why should send buttons stay disabled until the wallet session actually exists?

Quick Check
Single answer

How should the client treat a user rejecting a wallet prompt?

Sources

Solana Assistant

AI-powered documentation helper

Welcome to Solana Assistant

Ask specific questions about Solana development:

Ask specific questions for better results400px