web3modal: [bug] NextJS Hydration error causes web3modal to no longer open

Link to minimal reproducible example

https://github.com/mlenser/web3modal-w3m-account-button-already-used/tree/only-app-router-oct-10-2023

Steps to reproduce:

Summary

Thanks again for releasing 3.1.0. Our core issue is more clear now. It seems the web3modal stops working if there is a hydration error.

You can see the hydration error if you run the app locally. https://legacy.reactjs.org/docs/error-decoder.html/?invariant=418 and https://legacy.reactjs.org/docs/error-decoder.html/?invariant=423 will be shown in the console on the preview.

Every other part of our still works so this seems unique to web3modal. Though perhaps other apps are failing more gracefully? Not sure.

List of related npm package versions

Latest versions:

    "@web3modal/wagmi": "^3.1.0",
    "next": "13.5.4",
    "react": "^18",
    "react-dom": "^18",
    "viem": "^1.15.1",
    "wagmi": "^1.4.3"

About this issue

  • Original URL
  • State: closed
  • Created 9 months ago
  • Comments: 25 (12 by maintainers)

Most upvoted comments

I fixed it by wrapping my connect wallet button in this component <NonSSRWrapper></NonSSRWrapper>

so my component is looking like this

"use client";

import { useWeb3Modal } from "@web3modal/wagmi/react";
import { BiLogOut } from "react-icons/bi";
import { useAccount, useConnect } from "wagmi";
import NonSSRWrapper from "./NonSSRWrapper";

function shortenAddress(address) {
  // Check if the address is valid and has at least 8 characters
  if (address && address.length >= 8) {
    const start = address.slice(0, 4);
    const end = address.slice(-4);
    return `${start}....${end}`;
  }
  // Return the original address if it's not valid
  return address;
}

export default function ConnectWalletButton() {
  const { open } = useWeb3Modal();
  const { address, isConnecting } = useAccount();

  return (
    <>
      <NonSSRWrapper>
        <button
          disabled={isConnecting}
          className="py-1 px-[1em] rounded text-xs xl:text-sm border-2 border-purple disabled:opacity-60 disabled:pointer-events-none"
          onClick={() => open({ view: address ? "Account" : "Connect" })}
        >
          {address ? (
            <>
              <BiLogOut className="inline-block text-[1.3em] relative bottom-[.08em] mr-[.1em]" />{" "}
              {shortenAddress(address)}
            </>
          ) : (
            "Connect Wallet"
          )}
        </button>
      </NonSSRWrapper>
    </>
  );
}

this is NonSSRWrapper component looks like

import dynamic from "next/dynamic";
import React from "react";

const NonSSRWrapper = (props) => (
  <React.Fragment>{props.children}</React.Fragment>
);
export default dynamic(() => Promise.resolve(NonSSRWrapper), {
  ssr: false,
});

Great to hear! I’ll investigate once v4 is stable as the upgrade is quite large with viem, wagmi, and web3modal all having breaking changes that are related.

We’re very happy with App router. Our pages are now significantly smaller in size as App router handles the bundle much better and the App router experience is nice.

We can’t take advantage of the server on most pages as most of our data comes from the blockchain and wagmi can’t handle that currently, but App router is still worth the effort imo.

Thanks for sharing we’ll look into this.

So far I found that the hydration error blocks/removes when trying to create and append a child element to the body, this is what Web3Modal does and why it fails (I assume) during this error. We’ll investigate it.

Aside from the issue, I’m curious, how do you like the app router so far compared to pages?

My main concern is that runtime errors are difficult to fully eliminate. We can’t use any static tools like typescript, eslint, etc.

With some errors (at least the hydration one) the app fails gracefully and still works (except web3modal). Others the whole app fails/falls back to the NextJS error page.

Some other situations that I’ve encountered and would be be wary of:

  • [Not a runtime error] When a NextJS page “deopted into client-side rendering” the web3modal did not open. I solved this on our app by removing the useSearchParams from certain pages and then web3modal worked on those pages. I tried to create a reproduction of this issue this morning, but was unable to get the pages to deopt into client-side rendering in my reproduction repo. If that occurs web3modal doesn’t work. I’m not sure why since client should be ideal. I’ll try to create a reproduction of this.
  • [Runtime error (fails gracefully)] Hydration issues that we’ve discussed above. <div> in <p> cannot be statically checked in all cases so it could slip through, causing web3modal to not work unless every page is manually tested. I know hydration issues from wagmi aren’t your concern and that’s fair, but there are other types that could slip through.
  • [Runtime error (?)] API or blockchain response might return unexpected data. Personally, we’re typing our API with zod and have types for wagmi responses, but there may be cases where a runtime error occurs. I’m not sure if it’d fail gracefully or not.
  • [Runtime error (?)] Javascript errors such as BigInt/BigNumber. We have occasionally seen overflows/underflows and other cases of converting to/from, especially when we have user input. We’ve hopefully covered all cases, but I’ve seen some that fail gracefully in the past. I’m not sure how web3modal would handle them.

Cases that I’m not concerned about:

  • [Runtime error (hard fail)] throw new Error for example. The whole app seems to break/fallback to an error page so web3modal isn’t a concern here.