← 文章 / 编程开发
freeCodeCamp 7小时前 · 2026-10-02 22:28:08 · 5 阅读

免写 Recharts 模板:在 Next.js 应用中集成 shadcn 图表

在工单上看,图表好像是个小活儿。可一旦打开 Recharts 文档,你就会想起每个图表都需要多少配置:标签和颜色的配置对象、坐标轴、tooltip、图例、暗色模式下也不违和的配色,还有一个能正确自适应大小的容器。大多数人的做法是把上个项目里的代码整个复制过来,改改变量名。

shadcn/ui 的图表组件能省掉一部分工作,它在 Recharts 外面包了一层,自带主题配色和 tooltip。但每个图表你还是得手写。

于是我做了 ChartCN,一个免费开源的 shadcn 图表生成器。粘贴数据、选好图表类型,就能复制一个 TSX 文件,它只依赖 shadcn/ui 图表组件和 Recharts。项目采用 MIT 协议,不需要注册账号,也不用装额外的包。

本教程会带你在全新的 Next.js 应用里添加一个收入与支出对比的柱状图。我们会逐段讲解生成代码的作用,然后把图表接入在 Server Component 中加载的数据。这个工具帮你省去了打字的功夫,但第 5 步和第 6 步适用于任何 shadcn/ui 图表,不管它是生成的还是你自己写的。

前置要求:你需要熟悉 React 并安装了 Node.js。对 Next.js App Router 有基本了解会更有帮助,但每一步都有详细说明。

目录

你将构建什么

读完本文,你会有两个页面:

  • / 路由展示一个分组条形图,数据直接写在组件里。这是最快让图表上屏的方式。

  • /live 路由展示同样的图表,但数据来自服务器端的异步函数。接入真实数据库或 API 时用这个版本。

Shadcn Charts - Finished Bar Chart

该图表带有渐变条形、紧凑的坐标轴标签(如 40K 代替 40000)、图例,以及显示当月总额和各系列占比的提示框。

第一步:创建 Next.js 应用并初始化 shadcn/ui

新建应用并进入目录:

npx create-next-app@latest my-charts-app
cd my-charts-app

接受推荐的默认选项。你需要 TypeScript、Tailwind CSS 和 App Router。

接着,初始化 shadcn/ui:

npx shadcn@latest init

这条命令会完成三项你后续会依赖的工作:

  1. 创建 components.json,告知 shadcn/ui CLI 组件的存放位置。

  2. 添加 lib/utils.ts,其中包含 cn() 类名合并助手函数。

  3. 在 app/globals.css 中写入主题变量,包括五种图表颜色:--chart-1 到 --chart-5,并区分浅色和深色模式。

这五个图表变量至关重要。每个生成的图表都会使用它们,因此要修改整个应用的图表颜色,只需编辑一行 CSS。

第二步:添加 shadcn/ui 图表组件

现在添加图表组件:

npx shadcn@latest add chart

这会安装 recharts 并创建 components/ui/chart.tsx。该文件导出了生成代码中会看到的构建模块:

  • ChartContainer 封装了 Recharts 的 ResponsiveContainer,使图表填满父容器。

  • ChartConfig 是用于映射每个数据键到标签和颜色的对象类型。

  • ChartTooltip、ChartTooltipContent、ChartLegend 和 ChartLegendContent 是 Recharts 提示框和图例的样式化版本。

继续之前,先检查一下当前安装的 Recharts 版本:

npm ls recharts

ChartCN 生成的代码适配的是 Recharts 3。如果显示的是 2.x 版本,请升级:

npm install recharts@latest

要点: shadcn/ui 图表组件只是 Recharts 的一层薄封装,它并非取代 Recharts,而是为你的 Recharts 图表应用主题样式。

第 3 步:在 ChartCN 中生成图表组件

打开 ChartCN 条形图页面。数据面板包含三个选项卡:粘贴数据、上传文件和编辑表格。在粘贴数据选项卡中填入以下 CSV 内容:

Month,Revenue,Expenses
Jan,42000,31000
Feb,58000,34000
Mar,51000,29000
Apr,67000,38000
May,72000,41000
Jun,69000,37000

ChartCN 会将第一列视为分类轴(条形图的 x 轴),其余列视为数值系列。JSON、TSV 及 Markdown 表格格式同样适用,系统会自动识别格式。

Shadcn Charts - Bar Chart

在预览区上方设置以下选项:

  • 布局: 分组

  • 提示框: 细分数据

  • 代码: 内联数据

然后点击预览下方 chart.tsx 面板中的 复制组件。

Shadcn Charts - ChartCn Copy component

第 4 步:将图表添加到页面

在 components/revenue-chart.tsx 新建文件,并粘贴复制的代码。

不要将其保存为 components/ui/chart.tsx。虽然 ChartCN 输出的文件名为 chart.tsx,但该路径已存放第 2 步中的shadcn 组件,覆盖它会破坏应用中的所有图表。

生成的组件始终导出为 Chart,因此在引入时应使用更清晰的命名。请用以下内容替换 app/page.tsx:

// app/page.tsx
import { Chart as RevenueChart } from "@/components/revenue-chart"

export default function Home() {
  return (
    <main className="mx-auto max-w-3xl p-8">
      <h1 className="mb-6 text-2xl font-semibold">Revenue vs. expenses</h1>
      <RevenueChart />
    </main>
  )
}

运行 npm run dev,打开 http://localhost:3000,就能看到图表了。

要点:生成的图表其实就是项目里的一个普通组件,不需要任何配置,也没有需要同步维护的依赖包。

第 5 步:读懂生成的代码

这个文件现在归你了,有必要搞清楚每个部分的作用。下面是关键代码(有删减)。

文件开头是一条指令和一些导入:

// components/revenue-chart.tsx
"use client"

import { useId } from "react"
import { Bar, BarChart, CartesianGrid, Rectangle, XAxis, YAxis } from "recharts"
import {
  type ChartConfig,
  ChartContainer,
  ChartLegend,
  ChartLegendContent,
  ChartTooltip,
} from "@/components/ui/chart"

Recharts 需要测量 DOM 并处理鼠标事件,所以图表必须是 Client Component。而 app/page.tsx 只负责渲染图表,可以继续作为 Server Component。

接下来是数据和配置:

const data = [
  { Month: "Jan", Revenue: 42000, Expenses: 31000 },
  { Month: "Feb", Revenue: 58000, Expenses: 34000 },
  // ...
]

const chartConfig = {
  Revenue: { label: "Revenue", color: "var(--chart-1)" },
  Expenses: { label: "Expenses", color: "var(--chart-2)" },
} satisfies ChartConfig

chartConfig 的键必须与数据中的键一致。ChartContainer 会读取这份配置,并为每个键创建一个仅作用于当前图表的 CSS 变量:--color-Revenue 和 --color-Expenses。这些变量指向主题色,所以深色模式下图表会自动切换配色。

图表本身正是使用了这些变量:

<ChartContainer config={chartConfig} className="aspect-auto h-[350px] w-full">
  <BarChart accessibilityLayer data={data} barCategoryGap="30%" barGap={4}>
    {/* ...gradient <defs>, grid, axes, tooltip, legend... */}
    <Bar
      dataKey="Revenue"
      fill="var(--color-Revenue)"
      shape={(props) => <Rectangle {...props} fill={`url(#${uid}-fill-0)`} />}
      radius={[6, 6, 0, 0]}
      maxBarSize={36}
    />
  </BarChart>
</ChartContainer>

有几个细节值得注意:

  • 高度由 ChartContainer 上的 h-[350px] 设定。如需调整,在此处修改。

  • 渐变效果由 shape 属性绘制,而 fill 保持为纯色。因此,图例和 tooltip 中的圆点显示的是纯色而非渐变。

  • uid 来自 useId(),确保在同一页面渲染多个图表时,渐变 ID 保持唯一。

该文件还包含一个约 70 行的 ChartBreakdownTooltip 组件,用于显示总计及每个系列的百分比。如果你更倾向于使用标准的 shadcn tooltip,请在复制前在 ChartCN 中选择 Tooltip: Simple。这样整个文件的行数将从约 120 行缩减到约 50 行。

总结:在 shadcn/ui 图表中,颜色通过主题变量经由 chartConfig 流向 --color-<key> 变量。理解这一路径后,你就可以手动编辑任何图表了。

Step 6: 将实时数据作为 Prop 传入

对于演示而言,硬编码数据没问题。对于实际数据,请回到 ChartCN,将 Code 模式切换为 Data as prop,然后再次复制。将此版本保存为 components/revenue-chart-live.tsx。

图表标记不变。文件顶部有所变化:data 数组被类型取代,组件将 data 接受为 prop:

// components/revenue-chart-live.tsx
export type ChartRow = { Month: string; Revenue: number | null; Expenses: number | null }

export interface ChartProps {
  data: ChartRow[]
}

// ...chartConfig 未变...

export function Chart({ data }: ChartProps) {
  // ...与前相同的 JSX
}
序列的类型被设为 `number | null`,因为实际数据中可能存在空缺。值为 `null` 时,图表会显示为缺失的柱状,而非零值柱状。 接下来,创建一个在服务器端加载数据的函数:
// lib/get-revenue.ts
import type { ChartRow } from "@/components/revenue-chart-live"

export async function getRevenue(): Promise<ChartRow[]> {
  // 请将此处替换为实际的数据库查询或 API 调用。
  // 例如:const res = await fetch("https://api.example.com/revenue")
  return [
    { Month: "Jan", Revenue: 42000, Expenses: 31000 },
    { Month: "Feb", Revenue: 58000, Expenses: 34000 },
    { Month: "Mar", Revenue: 51000, Expenses: 29000 },
    { Month: "Apr", Revenue: 67000, Expenses: 38000 },
    { Month: "May", Revenue: 72000, Expenses: null },
  ]
}
然后在 Server Component 页面中调用它:
// app/live/page.tsx
import { Chart as RevenueChart } from "@/components/revenue-chart-live"
import { getRevenue } from "@/lib/get-revenue"

export default async function LivePage() {
  const data = await getRevenue()

  return (
    <main className="mx-auto max-w-3xl p-8">
      <h1 className="mb-6 text-2xl font-semibold">Revenue vs. expenses</h1>
      <RevenueChart data={data} />
    </main>
  )
}
打开 `http://localhost:3000/live`。你会看到五月份只显示了 Revenue 柱,旁边没有 Expenses 柱,因为该值为 `null`。 该页面在服务器端获取数据,仅将数据行发送到客户端组件。数据库凭据和 API 密钥因此保留在服务器端。当你替换为真实的 `fetch` 或数据库调用时,请查阅 Next.js 数据获取文档 以控制数据刷新频率。 核心要点:在 Server Component 中加载数据,在 Client Component 中渲染。导出的 `ChartRow` 类型可让 TypeScript 校验数据是否符合图表的预期结构。

常见问题及解决方法

类型错误,如 "Property 'itemSorter' does not exist"。这表明项目中安装的是 Recharts 2。运行 `npm install recharts@latest` 即可升级至 Recharts 3。

柱子是黑色的,图例的圆点不见了。你的 app/globals.css 里没有定义 --chart-1 到 --chart-5。重新运行 npx shadcn@latest init,或者从 shadcn/ui 主题文档中复制这些图表变量。

粘贴一个图表后所有图表都坏了。你很可能是把生成的文件覆盖保存到了 components/ui/chart.tsx。用 npx shadcn@latest add chart --overwrite 恢复它,并把你的图表换个名字保存。

同一页面的两个图表重名。每个生成的组件都导出为 Chart,导入时重命名即可,比如 import { Chart as SignupsChart } from "@/components/signups-chart"。

总结

  1. shadcn/ui 的图表组件是对 Recharts 的主题化封装。它不替代 Recharts,而且 ChartCN 的输出需要 Recharts 3。

  2. 颜色来自 CSS 变量。--chart-1 通过 chartConfig 映射为 --color-Revenue,所以暗色模式无需额外代码就能正常工作。

  3. 生成的图表要用独立的文件名保存。components/ui/chart.tsx 归 shadcn/ui 所有。

  4. 真实数据请用 Data 作为 prop。在 Server Component 中加载数据,再把带类型的行数据传给图表。

  5. null 表示“无数据”。空缺会渲染为空缺,而不是 0。

ChartCN 是一个 shadcn 图表生成器而非组件库,所以没有需要安装和维护更新的 ChartCN 包。你可以浏览它支持的全部 13 种 shadcn 图表,包括折线图、面积图、饼图、雷达图、KPI 卡片、瀑布图和热力图,它们都遵循本文同样的「粘贴、选择、复制」流程。

源码托管在 GitHub 上,采用 MIT 许可证。如果对你有帮助,点个 star 能让更多开发者发现它。

原始来源: freeCodeCamp

评论 (0)