9

I was trying to implement wagmi in my dapp, after I copied the example in the documentation https://wagmi.sh/examples/connect-wallet, I got this error: Error: Hydration failed because the initial UI does not match what was rendered on the server. Here is my code below.

import { WagmiConfig, createClient, defaultChains, configureChains } from "wagmi";

import { alchemyProvider } from "wagmi/providers/alchemy";
import { publicProvider } from "wagmi/providers/public";

import { CoinbaseWalletConnector } from "wagmi/connectors/coinbaseWallet";
import { InjectedConnector } from "wagmi/connectors/injected";
import { MetaMaskConnector } from "wagmi/connectors/metaMask";
import { WalletConnectConnector } from "wagmi/connectors/walletConnect";
import Profile from "../components/Profile";
import TheHead from "../components/TheHead";

// Configure chains & providers with the Alchemy provider.
// Two popular providers are Alchemy (alchemy.com) and Infura (infura.io)

// Pass client to React Context Provider
function App() {
  const { chains, provider, webSocketProvider } = configureChains(defaultChains, [
    alchemyProvider({ apiKey: process.env.RINKEBY_URL }),
    publicProvider(),
  ]);

  // Set up client
  const client = createClient({
    autoConnect: true,
    connectors: [
      new MetaMaskConnector({ chains }),
      new CoinbaseWalletConnector({
        chains,
        options: {
          appName: "wagmi",
        },
      }),
      new WalletConnectConnector({
        chains,
        options: {
          qrcode: true,
        },
      }),
      new InjectedConnector({
        chains,
        options: {
          name: "Injected",
          shimDisconnect: true,
        },
      }),
    ],
    provider,
    webSocketProvider,
  });

  return (
    <div>
      <TheHead />
      <WagmiConfig client={client}>
        <Profile />
      </WagmiConfig>
    </div>
  );
}

export default App;
import { useAccount, useConnect, useDisconnect, useEnsAvatar, useEnsName } from "wagmi";

export default function Profile() {
  const { address, connector, isConnected } = useAccount();
  const { data: ensAvatar } = useEnsAvatar({ addressOrName: address });
  const { data: ensName } = useEnsName({ address });
  const { connect, connectors, error, isLoading, pendingConnector } = useConnect();
  const { disconnect } = useDisconnect();

  if (isConnected) {
    return (
      <div>
        <Image src={ensAvatar} alt="ENS Avatar" height={70} width={70} />
        <div>{ensName ? `${ensName} (${address})` : address}</div>
        {/* <div>Connected to {connector.name}</div> */}
        <button onClick={disconnect}>Disconnect</button>
      </div>
    );
  }

  return (
    <div>
      {connectors.map((connector) => (
        <button disabled={!connector.ready} key={connector.id} onClick={() => connect({ connector })}>
          {connector.name}
          {!connector.ready && " (unsupported)"}
          {isLoading && connector.id === pendingConnector?.id && " (connecting)"}
        </button>
      ))}

      {error && <div>{error.message}</div>}
    </div>
  );
}

6 Answers 6

5

If you set autoConnect: to false it should resolve your issue.

2

That solution nakkakist gave is perfect. Just another one, so anyone who runs into this issue can have a choice:

const [isDefinitelyConnected, setIsDefinitelyConnected] = useState(false);
const { address, isConnected } = useAccount();

useEffect(() => {
    if (isConnected) {
      setIsDefinitelyConnected(true);
    } else {
      setIsDefinitelyConnected(false);
    }
  }, [address]);

This way, we make sure that status is correctly updated when we have an address for the connected user.

1

Another solution is to prepare _isConnected and _connectors states by yourself, and update those states after the component is mounted.

For example,

export default function Profile() {
  const { address, connector, isConnected } = useAccount();
  const { connect, connectors, error, isLoading, pendingConnector } = useConnect();
  const [_isConnected, _setIsConnected] = useState(false);
  const [_connectors, _setConnectors] = useState([]);
  ...

  useEffect(() => {
    _setIsConnected(isConnected);
  }, [isConnected]);

  useEffect(() => {
    _setConnectors(connectors);
  }, [connectors]);

  if (_isConnected) {
    return (
      <div>
       ...
      </div>
    );
  }

  return (
    <div>
     {_connectors.map((connector) => (
      ...
    </div>
  );
}

In this way, you can keep autoConnect: to true

1

Based on the previous answers, this little thing worked for me

    const [isReady, setIsReady] = useState(false);
    
    useEffect(() => setIsReady(true), []);

It just sets isReady to true on hydration. Then I use this as a condition for loading the connect button and things seem to become sane once again.

1

It is advisable to create a reusable custom hook to maintain the autoconnect logic and enable its reuse in multiple locations as needed.

Custom Hook

import { useEffect, useLayoutEffect, useState } from "react";
import { useAccount } from "wagmi";

export const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;

export function useIsConnected() {
    const [isConnected, setIsConnected] = useState(false);
    const { isConnected: _isConnected } = useAccount();
    useIsomorphicLayoutEffect(() => {
        setIsConnected(_isConnected);
    }, [_isConnected]);

    return isConnected;
}

Usage:

export function MyComponent() {
    const isConnected = useIsConnected();

    return <div>{isConnected ? "Connected" : "Not connected"}</div>;
}

1
  • This is perfect, it fixes the hydration issue while mantaining the autoconnect feature
    – Agu Dondo
    Commented Aug 1, 2023 at 8:53
0

If you are coming back using wagmi with viem and Nextjs. The solution for me was using dynamic components.

Every component that is interacting with wagmi hooks needs to be rendered that way. So when using a component simply:

import dynamic from "next/dynamic";

Then:

const MyComponentUsingWagmi = dynamic(()=>import('path/to/my/component'),{ssr:false});

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.