You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 15使用shadcn-ui Card组件出现Hydration不匹配错误求助

解决Next.js 15中shadcn-ui Card组件的Hydration不匹配错误

核心问题分析

你遇到的「Hydration failed」错误本质是服务端渲染的HTML与客户端渲染的DOM结构不一致,结合代码来看,主要有以下几个触发点:


解决方案

1. 误用Lucide的Link组件作为路由链接

你当前导入的Link来自lucide-react,这是图标组件而非Next.js的路由跳转组件,将Card包裹在这个图标组件内会生成无效的DOM结构,直接导致服务端与客户端渲染结果不匹配。

修改方式:
替换路由Link组件,改用Next.js官方的next/link:

// 替换原导入语句
import Link from "next/link";
// 如果需要Link图标,单独导入并改名
// import { Link as LinkIcon } from "lucide-react";

同时给Next.js的Link添加block类,确保它正确包裹整个Card:

<Link
  href={`/blog/${post.metadata.category}/${post.slug}`}
  key={post.slug}
  className="block"
>
  <CardCategory
    title={post.metadata.title}
    summary={post.metadata.summary}
    date={post.metadata.publishedAt}
  />
</Link>

2. 修复notFound()的判断逻辑

filter方法返回的是空数组而非null,原代码中if (!posts)永远不会触发,导致没有对应分类的文章时,服务端渲染空页面,客户端却不触发404,造成内容差异。

修改方式:

// 原判断
// if (!posts) { notFound(); }
// 改为判断数组长度
if (posts.length === 0) {
  notFound();
}

3. 统一日期排序逻辑

直接用new Date()比较日期,可能因服务端与客户端的时区差异导致排序结果不同,进而使DOM元素顺序不一致。

修改方式:
使用Date.parse()统一日期解析逻辑,确保排序结果一致:

const sortedPosts = [...posts].sort((a, b) => 
  Date.parse(b.metadata.publishedAt) - Date.parse(a.metadata.publishedAt)
);

建议直接在getBlogPosts函数中完成排序,减少组件内的动态逻辑,进一步避免 hydration 差异。

4. 检查Card组件的客户端依赖(可选)

如果shadcn-ui的Card组件包含客户端专属API(如useEffect、useState),需要在CardCategory组件顶部添加客户端指令:

"use client"; // 仅当组件需要客户端API时添加

import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle,
} from "./ui/card";
// ... 其余代码不变

修改后的完整页面代码示例

import CardCategory from "@/components/CardCategory";
import Container from "@/components/Container";
import Link from "next/link";
import { notFound } from "next/navigation";
import { use } from "react";
import { getBlogPosts } from "../utils";

export function generateStaticParams() {
  let posts = getBlogPosts();
  // 去重分类,避免生成重复的静态路由参数
  const uniqueCategories = [...new Set(posts.map(post => post.metadata.category))];
  
  return uniqueCategories.map((category) => ({
    category,
  }));
}

export async function generateMetadata({
  params,
}: {
  params: Promise<{ category: string }>;
}) {
  let { category } = await params;

  return {
    title: category.toLocaleUpperCase(),
    description: `All articles regarding ${category}`, // 修正拼写错误reagarding -> regarding
  };
}

export default function Page({
  params,
}: {
  params: Promise<{ category: string }>;
}) {
  let { category } = use(params);

  let posts = getBlogPosts().filter(
    (post) => post.metadata.category === category
  );

  if (posts.length === 0) {
    notFound();
  }

  const sortedPosts = [...posts].sort((a, b) => 
    Date.parse(b.metadata.publishedAt) - Date.parse(a.metadata.publishedAt)
  );

  return (
    <>
      <Container>
        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mt-10">
          {sortedPosts.map((post) => (
            <Link
              href={`/blog/${post.metadata.category}/${post.slug}`}
              key={post.slug}
              className="block"
            >
              <CardCategory
                title={post.metadata.title}
                summary={post.metadata.summary}
                date={post.metadata.publishedAt}
              />
            </Link>
          ))}
        </div>
      </Container>
    </>
  );
}

内容的提问来源于stack exchange,提问作者Gökhan Ekici

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 00:39:52