NextJS项目添加shadcn导航栏时出现Hydration错误求助
修复Next.js中ShadCN导航栏的Hydration错误
核心问题分析
Hydration错误本质是服务器渲染的DOM与客户端初始化后的DOM结构不匹配,你的代码存在几个关键问题:
- 嵌套的
Link组件:Next.js不允许Link嵌套,会导致两端解析的DOM结构不一致 NavigationMenu使用不规范:直接在NavigationMenuItem内使用Next.js的Link,不符合组件规范- 移动端logo放置位置不合理:嵌套在
Sheet内部可能引发渲染结构差异
具体修复步骤
1. 移除嵌套的Link组件
原代码中嵌套Link是错误写法,修改为单一层级:
// 错误写法 <Link className="mr-2 mb-1 items-center lg:flex" href="#"> <Link href={"/"}>...</Link> </Link> // 修改后 <Link href="/" className="mr-2 mb-1 items-center lg:flex"> <Image src="/logo-makak-horB.png" className="lg:flex hidden h-auto" height={20} width={180} alt="logo makak black horizontal" /> <span className="sr-only">Acme Inc</span> </Link>
2. 规范NavigationMenu的使用
NavigationMenuItem内部必须用NavigationMenuLink包裹Next.js的Link,确保状态一致:
// 错误写法 <NavigationMenuItem> <Link href="#" className="...">Services</Link> </NavigationMenuItem> // 修改后 <NavigationMenuItem> <NavigationMenuLink asChild> <Link href="#" className="group inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-gray-100 hover:text-gray-900 focus:bg-gray-100 focus:text-gray-900 focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-gray-100/50 data-[state=open]:bg-gray-100/50 " > Services </Link> </NavigationMenuLink> </NavigationMenuItem>
将所有直接放在NavigationMenuItem中的Link都按此方式修改。
3. 调整移动端logo位置
将移动端logo从Sheet内部移到外部,避免服务器端渲染时的结构冲突:
// 移到Sheet组件外部 <Image className="lg:hidden" src="/logo-makak-horB.png" height={20} width={180} alt="logo makak black horizontal" /> <Sheet> {/* 移除Sheet内部的重复logo代码 */} <div className="lg:hidden flex justify-between w-full">...</div> <SheetContent>...</SheetContent> </Sheet>
4. 可选:用Suspense缓冲客户端渲染
如果上述修改后仍有错误,可在使用Navbar的地方用Suspense包裹,给客户端渲染留缓冲:
import { Suspense } from 'react'; import { Navbar } from '@/components/Navbar'; function Layout({ children }) { return ( <> <Suspense fallback={<div className="h-20 w-full bg-stone-100"></div>}> <Navbar /> </Suspense> <main>{children}</main> </> ); }
修复后的完整Navbar代码
"use client"; import { Sheet, SheetTrigger, SheetContent } from "@/components/ui/sheet"; import Link from "next/link"; import { Button } from "./ui/button"; import Image from "next/image"; import { NavigationMenu, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, } from "@/components/ui/navigation-menu"; export function Navbar() { return ( <div className="fixed top-0 w-full z-50"> <header className="flex h-20 shadow-xl opacity-95 border-b-2 z-50 border-orange-400 bg-stone-100 shrink-0 items-center px-4 md:px-20"> {/* 移动端logo移至Sheet外部 */} <Image className="lg:hidden" src="/logo-makak-horB.png" height={20} width={180} alt="logo makak black horizontal" /> <Sheet> <div className="lg:hidden flex justify-between w-full"> <Button asChild className="lg:hidden" size="icon" variant="outline"> <SheetTrigger className="flex"> <MenuIcon className="h-6 w-6" /> <span className="sr-only">Toggle navigation menu</span> </SheetTrigger> </Button> </div> <SheetContent side="left"> <Link className="mr-6 lg:hidden flex" href="/"> <Image src="/logo-makak-horB.png" height={20} width={180} alt="logo makak black" /> <span className="sr-only">Acme Inc</span> </Link> <div className="grid gap-2 py-6"> <Link className="flex w-full items-center py-2 text-lg font-semibold" href="/" > Home </Link> <Link className="flex w-full items-center py-2 text-lg font-semibold" href="/about" > About </Link> <Link className="flex w-full items-center py-2 text-lg font-semibold" href="/services" > Services </Link> <Link className="flex w-full items-center py-2 text-lg font-semibold" href="/contact" > Contact </Link> </div> </SheetContent> </Sheet> {/* 修复嵌套Link */} <Link href="/" className="mr-2 mb-1 items-center lg:flex"> <Image src="/logo-makak-horB.png" className="lg:flex hidden h-auto" height={20} width={180} alt="logo makak black horizontal" /> <span className="sr-only">Acme Inc</span> </Link> <div className="flex justify-between w-full items-center"> <nav className="ml-0 hidden lg:flex gap-6"> <NavigationMenu> <NavigationMenuList> <NavigationMenuItem> <NavigationMenuLink asChild> <Link href="/" className="group inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-gray-100 hover:text-gray-900 focus:bg-gray-100 focus:text-gray-900 focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-gray-100/50 data-[state=open]:bg-gray-100/50 " > Home </Link> </NavigationMenuLink> </NavigationMenuItem> <NavigationMenuItem> <NavigationMenuTrigger className="group bg-opacity-0 inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-gray-100 hover:text-gray-900 focus:bg-gray-100 focus:text-gray-900 focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-gray-100/50 data-[state=open]:bg-gray-100/50 "> About </NavigationMenuTrigger> <NavigationMenuContent className="p-10"> About content here </NavigationMenuContent> </NavigationMenuItem> <NavigationMenuItem> <NavigationMenuLink asChild> <Link href="/services" className="group inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-gray-100 hover:text-gray-900 focus:bg-gray-100 focus:text-gray-900 focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-gray-100/50 data-[state=open]:bg-gray-100/50 " > Services </Link> </NavigationMenuLink> </NavigationMenuItem> <NavigationMenuItem> <NavigationMenuLink asChild> <Link href="/contact" className="group inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-gray-100 hover:text-gray-900 focus:bg-gray-100 focus:text-gray-900 focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-gray-100/50 data-[state=open]:bg-gray-100/50 " > Contact </Link> </NavigationMenuLink> </NavigationMenuItem> </NavigationMenuList> </NavigationMenu> </nav> <div className="hidden lg:flex text-sm "> <button className="p-2 rounded-lg mr-2 tracking-wider">PL</button> <button className="bg-orange-400 p-2 rounded-lg tracking-wider"> ENG </button> </div> </div> </header> </div> ); } function MenuIcon(props) { return ( <svg {...props} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" > <line x1="4" x2="20" y1="12" y2="12" /> <line x1="4" x2="20" y1="6" y2="6" /> <line x1="4" x2="20" y1="18" y2="18" /> </svg> ); }
内容的提问来源于stack exchange,提问作者Franciszek O
相关产品推荐
相关产品推荐

