Skip to main content
@dynamic-labs-sdk/react-hooks is a thin React layer over @dynamic-labs-sdk/client. Every hook wraps a client function or a piece of client state and re-renders your component when the underlying value changes, so you rarely need to wire up useState/useEffect yourself. This page explains the model: how hooks are named, which functions get one, and the three shapes a hook can take. For the setup steps (DynamicProvider) and a catalog of the headline state hooks, see the React Hooks reference.
Before this: create and initialize a Dynamic client (see Creating a Dynamic Client, Initializing the Dynamic Client) and wrap your tree in DynamicProvider.

The naming rule

Each hook has the same name as the client function it wraps, prefixed with use. The rest of the name is unchanged, so once you know the function you know the hook:
Client function (@dynamic-labs-sdk/client)Hook (@dynamic-labs-sdk/react-hooks)
signMessageuseSignMessage
getFlowuseGetFlow
getMultichainTokenBalancesuseGetMultichainTokenBalances
logoutuseLogout
updateUseruseUpdateUser
A handful of state hooks read a derived value rather than a single function and are named for the value they expose — for example useUser, useGetWalletAccounts, and useInitStatus.

Which functions get a hook

Not every function in the client needs a React wrapper. The rules:
  • Every async function gets a hook. Anything you would await — reads like getFlow, writes like signMessage — has a matching hook so you get loading and error state for free.
  • Reactive client state is exposed as a state hook. Values that the client keeps in memory and updates over time — the current user, wallet accounts, registered wallet providers, init status, session expiry — each have a hook that re-renders on change.
  • A sync function gets a hook only when its return value can change reactively. Most synchronous getters are pure and don’t need a hook — just call them. But some read live client state that changes after events (for example the linked social accounts derived from the current user, or the available wallet providers). Those get a hook so your component re-renders when the value changes; calling the bare function would give you a stale snapshot. Examples: useGetUserSocialAccounts and useGetAvailableWalletProvidersData.
  • Events are subscribed to with useOnEvent. Any client event can be observed for a component’s lifetime — see useOnEvent.
  • Purely synchronous, non-reactive functions do not get a hook. Call them directly inside your component or an event handler.
If you can’t find a hook for a function, it’s almost always because the function is synchronous and its result doesn’t change over time — import it from @dynamic-labs-sdk/client and call it directly. You can wrap the call in useMemo so it only recomputes when its dependencies change:
import { useMemo } from 'react';
import { isHardwareWalletAccount } from '@dynamic-labs-sdk/client';
import { useGetWalletAccounts } from '@dynamic-labs-sdk/react-hooks';

function HardwareWalletCount() {
  const { data: walletAccounts = [] } = useGetWalletAccounts();

  const hardwareCount = useMemo(
    () =>
      walletAccounts.filter((wa) =>
        isHardwareWalletAccount({ walletAccount: wa })
      ).length,
    [walletAccounts]
  );

  return <p>{hardwareCount} hardware wallet(s) connected</p>;
}
isHardwareWalletAccount is a pure check on a wallet account object — it doesn’t change for a given input and has no hook. The reactivity comes from useGetWalletAccounts, which re-renders on connect/disconnect. useMemo avoids recomputing the filter on unrelated renders. This pattern works for any sync client function: pair a state hook for the reactive dependency with useMemo to derive the value you need.

The three shapes of a hook

A hook’s shape follows the function it wraps. All three shapes return TanStack Query result objects ({ data, isLoading, error, refetch, … }), so the loading, error, and refetch ergonomics are the standard ones you may already know.

Query hooks (async reads)

Wrap async read functions like getNativeBalance or getMultichainTokenBalances. They fetch on mount and return the full query result — data, isLoading, isFetching, error, refetch, and so on. Read hooks take their arguments as pseudo-required: the parameter is typed to accept undefined, and the hook stays disabled (no fetch) until you pass a real value. This lets you call the hook unconditionally — as the Rules of Hooks require — while waiting for an upstream value like a wallet account to load.
import { useGetNativeBalance } from '@dynamic-labs-sdk/react-hooks';

function WalletBalance({ walletAccount }) {
  // Disabled until `walletAccount` is defined, then fetches automatically.
  const { data: nativeBalance, isLoading } = useGetNativeBalance({ walletAccount });

  if (isLoading) return <p>Loading…</p>;
  return <p>Balance: {nativeBalance?.balance ?? '0'}</p>;
}

Mutation hooks (async actions)

Wrap async write functions like signMessage, logout, or updateUser. They don’t run on mount — you trigger them by calling mutate (or mutateAsync) with the function’s arguments. They return mutate, mutateAsync, isPending, data, error, and reset.
import { useSignMessage } from '@dynamic-labs-sdk/react-hooks';

function SignButton({ walletAccount }) {
  const { mutate: signMessage, data: signResult, isPending } = useSignMessage();

  return (
    <>
      <button
        onClick={() => signMessage({ walletAccount, message: 'gm' })}
        disabled={isPending}
      >
        {isPending ? 'Signing…' : 'Sign'}
      </button>
      {signResult?.signature && <code>{signResult.signature}</code>}
    </>
  );
}
Pass onSuccess / onError through mutateParams to react to the result:
const { mutate: signMessage } = useSignMessage({
  mutateParams: {
    onSuccess: ({ signature }) => console.log('Signed:', signature),
    onError: (error) => console.error(error),
  },
});
Mutation hooks that change server-side state automatically refetch the related queries when they succeed. For example, useDeleteMfaDevice refetches useGetMfaDevices, and useRevokeRegisteredDevice refetches useGetRegisteredDevices. You don’t need to call refetch yourself — the data stays in sync.

State hooks (reactive values)

Wrap reactive client state. They take no fetch arguments and return the same TanStack Query result as query hooks — data, isLoading, error, refetch, and so on. The hook subscribes to the relevant client events and re-renders your component whenever the value changes.
import { useUser, useGetWalletAccounts } from '@dynamic-labs-sdk/react-hooks';

function Profile() {
  const { data: user } = useUser(); // re-renders on sign-in, sign-out, profile update
  const { data: walletAccounts } = useGetWalletAccounts(); // re-renders on connect/disconnect

  if (!user) return <SignInButton />;
  return <p>{user.email} · {walletAccounts?.length ?? 0} wallets</p>;
}

Hooks vs. calling the function directly

Both the hooks package and the client are public API. Reach for a hook when a component should react to the value or you want loading/error state managed for you. Call the client function directly for one-shot work where you don’t need re-renders — for example reading the current value inside an event handler.
import { getWalletAccounts } from '@dynamic-labs-sdk/client';

function ExportButton() {
  const handleExport = () => {
    const walletAccounts = getWalletAccounts();
    download(walletAccounts);
  };

  return <button onClick={handleExport}>Export wallets</button>;
}
Client functions resolve the client automatically via getDefaultClient() — no need to pass it explicitly. For the same value as reactive state that re-renders the component, use useGetWalletAccounts instead.