NextJS Hydration失败求助:服务端与客户端UI渲染不匹配
Next.js 水化错误(Hydration Failed)排查与解决
错误信息
Error: Hydration failed because the initial UI does not match what was rendered on the server.
Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
控制台警告
Warning: Expected server HTML to contain a matching <div> in <div>. at div at SendTransaction at div at MyApp (webpack-internal:///./pages/_app.tsx:39:11) at PathnameContextProviderAdapter (webpack-internal:///./node_modules/next/dist/shared/lib/router/adapters.js:62:11) at ErrorBoundary (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:301:63) at ReactDevOverlay (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:850:919) at Container (webpack-internal:///./node_modules/next/dist/client/index.js:62:1) at AppContainer (webpack-internal:///./node_modules/next/dist/client/index.js:172:11) at Root (webpack-internal:///./node_modules/next/dist/client/index.js:347:11)
项目文件代码
comps/transaction.tsx(已简化)
import * as React from 'react'; export function SendTransaction() { return <h1>TEST!</h1> }
pages/index.tsx
import type { NextPage } from 'next'; import { ConnectKitButton } from 'connectkit'; const Home: NextPage = () => { return ( <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100vh', }} > <ConnectKitButton /> </div> ); }; export default Home;
pages/_app.tsx
import { WagmiConfig, createClient, useAccount, useConnect, useDisconnect } from 'wagmi'; import { mainnet, polygon, optimism, arbitrum } from 'wagmi/chains'; import { ConnectKitProvider, getDefaultClient } from 'connectkit'; import { SendTransaction } from '../comps/transaction'; import React from 'react'; const client = createClient( getDefaultClient({ appName: 'ConnectKit Next.js demo', //infuraId: process.env.NEXT_PUBLIC_INFURA_ID, //alchemyId: process.env.NEXT_PUBLIC_ALCHEMY_ID, chains: [mainnet, polygon, optimism, arbitrum], }) ); function MyApp({ Component, pageProps }: AppProps) { const { isConnected } = useAccount(); if (isConnected) { return ( <div> {/* Account content goes here */} <SendTransaction /> </div> ) } return ( <WagmiConfig client={client}> <ConnectKitProvider> <Component {...pageProps} /> </ConnectKitProvider> </WagmiConfig> ); } export default MyApp;
问题关键点
移除_app.tsx中以下代码块后,应用恢复正常:
const { isConnected } = useAccount(); if (isConnected) { return ( <div> {/* Account content goes here */} <SendTransaction /> </div> ) }
原因分析
- 上下文范围错误:
useAccount()被放在了WagmiConfig组件外层,而wagmi的hooks必须在WagmiConfig提供的上下文环境中才能正常工作,导致状态读取异常。 - 服务器与客户端状态不一致:服务器渲染时无法获取钱包连接状态,
isConnected返回false,渲染包含首页组件的结构;客户端水化阶段,若用户已连接钱包,isConnected变为true,会渲染完全不同的DOM结构,导致服务器输出的HTML与客户端UI不匹配,触发水化错误。
解决方案
修改pages/_app.tsx,将状态判断逻辑移到WagmiConfig上下文内,并通过useEffect延迟客户端状态判断:
import { WagmiConfig, createClient, useAccount } from 'wagmi'; import { mainnet, polygon, optimism, arbitrum } from 'wagmi/chains'; import { ConnectKitProvider, getDefaultClient } from 'connectkit'; import { SendTransaction } from '../comps/transaction'; import React, { useEffect, useState } from 'react'; const client = createClient( getDefaultClient({ appName: 'ConnectKit Next.js demo', chains: [mainnet, polygon, optimism, arbitrum], }) ); // 封装上下文内的内容组件 const AppContent = ({ Component, pageProps }) => { const { isConnected } = useAccount(); const [clientReady, setClientReady] = useState(false); // 仅在客户端水化完成后判断连接状态 useEffect(() => { setClientReady(true); }, []); if (!clientReady) { // 服务器渲染和水化初期显示与服务器输出一致的内容 return <div>Loading...</div>; } if (isConnected) { return ( <div> <SendTransaction /> </div> ); } return <Component {...pageProps} />; }; function MyApp({ Component, pageProps }: AppProps) { return ( <WagmiConfig client={client}> <ConnectKitProvider> <AppContent Component={Component} pageProps={pageProps} /> </ConnectKitProvider> </WagmiConfig> ); } export default MyApp;
核心逻辑
- 将状态判断放在
WagmiConfig上下文内,确保useAccount能正常读取钱包状态。 - 使用
useEffect延迟客户端状态判断,避免服务器与客户端的DOM结构差异,彻底解决水化错误。
内容的提问来源于stack exchange,提问作者Willem Ghijsen
相关产品推荐
相关产品推荐

