← 返回蜂巢洞察

如何在不编写Recharts样板代码的情况下,将shadcn图表添加到Next.js应用程序中

在任务列表中,创建图表似乎是一项简单的任务。但当你打开 Recharts 的文档后,就会发现其实每个图表的设置都需要耗费不少精力:需要为标签和颜色配置相应的对象、设置坐标轴、添加工具提示、设计图例、选择适合深色模式的配色方案,还要确保图表容器能够正确地调整大小。我们大多数人都会从上一个项目中复制这些设置内容,然后重新命名相关变量即可。 shadcn/ui 图表组件在一定程度上减轻了这些工作负担。它为 Recharts 提供了与主题风格相匹配的配色方案和工具提示功能,但用户仍然需要手动编写每个图表的代码。 因此,我开发了 ChartCN——这个免费且开源的 shadcn 图表生成工具 。你只需粘

在任务列表中,创建图表似乎是一项简单的任务。但当你打开 Recharts 的文档后,就会发现其实每个图表的设置都需要耗费不少精力:需要为标签和颜色配置相应的对象、设置坐标轴、添加工具提示、设计图例、选择适合深色模式的配色方案,还要确保图表容器能够正确地调整大小。我们大多数人都会从上一个项目中复制这些设置内容,然后重新命名相关变量即可。

shadcn/ui 图表组件在一定程度上减轻了这些工作负担。它为 Recharts 提供了与主题风格相匹配的配色方案和工具提示功能,但用户仍然需要手动编写每个图表的代码。

因此,我开发了 ChartCN——这个免费且开源的 shadcn 图表生成工具。你只需粘贴数据、选择所需的图表类型,然后复制一个仅依赖于 shadcn/ui 图表组件和 Recharts 的 TSX 文件即可。ChartCN 使用 MIT 许可协议,使用时无需注册账户或下载任何额外插件。

在本教程中,你将学习如何在一个新的 Next.js 应用程序中添加收入与支出的柱状图。我们会详细解释生成代码的每一部分的作用,然后教你如何将这个图表与服务器组件中加载的数据关联起来。虽然这个工具可以节省你的输入工作量,但第 5 步和第 6 步中的操作方法适用于任何使用 shadcn/ui 图表组件的情况,无论你是通过它生成的图表,还是自己手动编写的代码。

先决条件:你需要熟悉 React,并且已经安装了 Node.js。对 Next.js 应用程序的路由系统有基本了解会更有帮助,但每个步骤都会被详细解释。

目录

你将构建什么

完成本教程后,你将会得到两个页面:

  • / 页面会显示一个直接在组件中编写数据的分组柱状图。这是将图表展示在屏幕上最快的方法。

  • /live 页面会显示相同的图表,但数据是从服务器端运行的异步函数中获取的。这种版本适用于与真实数据库或 API 进行交互的情况。

Shadcn Charts - 完成的柱状图

该图表使用了渐变条、简洁的轴标签(显示为“40K”而非“40000”)、图例,以及一个工具提示框,用于显示当月的总数据及各数据系列所占的比例。

步骤 1:创建 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 到 ,并且为浅色模式和深色模式分别设置了不同的颜色值。

这五种图表颜色非常重要。所有生成的图表都会使用这些颜色设置,因此如果您想要更改整个应用程序中所有图表的颜色,只需修改一行 CSS 代码即可。

步骤 2:添加 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

如果生成的代码中使用的 Recharts 版本是 2.x 系列,那么请升级到最新版本:

npm install recharts@latest

总结: shadcn/ui 图表组件实际上只是在 Recharts 的基础上添加了一层自定义样式,它并不会替代 Recharts 本身,而是为 Recharts 提供了统一的主题风格。

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

打开 ChartCN 条形图页面。数据面板中有三个选项卡:粘贴数据、上传文件 和 编辑表格。请将以下 CSV 数据粘贴到 粘贴数据 选项卡中:

月份,收入,支出
1月,42000,31000
2月,58000,34000
3月,51000,29000
4月,67000,38000
5月,72000,41000
6月,69000,37000

ChartCN会将第一列视为分类轴(对于柱状图来说就是X轴),而其他列则被视为数值序列。JSON、TSV和Markdown格式的表格也同样适用,系统会自动识别这些格式。

Shadcn Charts - 柱状图

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

  • 布局: 分组显示

  • 工具提示: 显示详细信息

  • 代码展示方式: 内嵌数据展示

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

Shadcn Charts - 复制组件

步骤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 (
    
收入与支出对比图

运行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结构并处理鼠标事件,因此该图表必须是一个客户端组件。而app/page.tsx文件中的代码可以继续作为服务器端组件使用,因为它仅仅负责渲染图表而已。

接下来是数据和配置信息:

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

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

chartConfig中的键值对必须与您数据文件中的键值对相匹配。ChartContainer会读取这些配置信息,并为每个配置项生成一个仅在该图表中生效的CSS变量,例如--color-Revenue和。这些变量用于指定图表的颜色,因此当系统处于深色模式时,图表的颜色也会自动发生变化。

图表本身会使用这些变量来生成视觉效果:

<ChartContainer config={chartConfig} className="aspect-auto h-[350px] w-full">
  <BarChart accessibilityLayer data={data} barCategoryGap="30%" barGap={4}>
    {/* ...梯度效果、网格布局、坐标轴、工具提示、图例等元素... */}
    <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>

有几点需要注意:

  • 高度是通过h-[350px]这个属性来设置的,因此您可以在ChartContainer中修改这个值。

  • 梯度效果是由shape属性实现的,而fill属性则用于设置纯色填充。这就是为什么图例和工具提示中的颜色是纯色而非渐变色的原因。

  • uid这个变量是通过useId()函数生成的,因此如果在同一页面上渲染两个图表,它们的梯度ID也会保持唯一性。

该文件还包含了一个长度约为70行的ChartBreakdownTooltip组件,该组件可以显示总数以及各数据系列的占比。如果您更喜欢使用标准的shadcn tooltip组件,可以在复制代码之前在ChartCN配置中选择Tooltip: Simple选项。这样,整个文件的代码行数就会从大约120行减少到50行左右。

总结:在shadcn/ui的图表系统中,颜色是通过主题变量、chartConfig配置文件,以及--color-这类变量来传递的。了解了这一数据流动路径后,您就可以手动修改任何图表的样式了。

步骤6:将实时数据作为属性传递

对于演示用途来说,使用硬编码的数据也是可以的。但如果是处理真实数据,就需要回到ChartCN设置界面,将Code选项改为Data as prop,然后再进行代码复制。将修改后的文件保存为components/revenue-chart-live.tsx。

图表的标记结构并没有发生变化,只有文件的开头部分有所调整:原来的data数组被替换成了一个类型定义,同时该组件也接受data作为属性进行传入:

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

export interface ChartProps {
  data: ChartRow[]
}

// ...其他配置信息保持不变...

export function Chart({ data }: ChartProps) {
  // ...与之前相同的JSX代码
}

这些数据被表示为数字 | null的形式,因为实际数据中存在缺失值。当值为null时,它会在图表中显示为缺失的条形,而不是零值的条形。

现在创建一个函数,用于从服务器加载数据:

// lib/get-revenue.ts
import type { ChartRow } from 「@components/revenue-chart-live」

export async function getRevenue(): Promise {
  // 请用您的数据库查询或API调用替换此代码。
  // 例如:const res = await fetch("https://api.example.com/revenue")
  return [
    { Month: "1月", Revenue: 42000, Expenses: 31000 },
    { Month: "2月", Revenue: 58000, Expenses: 34000 },
    { Month: "3月", Revenue: 51000, Expenses: 29000 },
    { Month: "4月", Revenue: 67000, Expenses: 38000 },
    { Month: "5月", Revenue: 72000, Expenses: null },
  ]
}

然后从服务器组件页面中调用这个函数:

// 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 (
    

收入与支出对比

) }

打开http://localhost:3000/live,会看到收入的条形图,而旁边的支出条形图为空,因为其值为null。

该页面会从服务器获取数据,并仅将数据传递给客户端组件。您的数据库凭证和API密钥始终保存在服务器上。当您使用真实的fetch函数或数据库查询来获取数据时,请参考Next.js数据获取文档,以控制数据更新的频率。

总结:请将数据加载操作放在服务器组件中,而渲染工作则由客户端组件完成。通过导出ChartRow类型,TypeScript能够确保您提供的数据符合图表的需求。

常见问题及解决方法

出现“'itemSorter'属性不存在”之类的类型错误。说明您的项目中安装的是Recharts 2版本。请运行npm install recharts@latest将其升级到Recharts 3版本。

条形图显示为黑色,且图例中的点也消失了。可能是您的app/globals.css文件中未定义--chart-1至这些变量。请再次运行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”作为属性来传递实际数据。只需将这些数据加载到服务器组件中,然后将其传递给图表即可。

  5. null表示“没有数据”。在这种情况下,空白区域会以空白的形式显示,而不会被显示为0值。

由于ChartCN实际上是一个图表生成工具,而不是一个组件库,因此不存在需要安装或更新的ChartCN包。你可以查看它所支持的13种图表类型,包括折线图、面积图、饼图、雷达图、KPI看板图、瀑布图以及热力图。使用这些图表时,都可以遵循与此处相同的使用流程:选择数据、粘贴到相应位置即可。

该工具的源代码托管在GitHub上,采用MIT许可证。如果这个工具对你有帮助,请给它点个星,这样其他开发者就能更容易地找到它。

相关文章

技术实践

文章:从可重复使用到可再生:重新审视那些被共享的UI组件库

“可再生设计”这一概念改变了组件的重用模式。一种完善的設计系统能够根据需求生成大多数标准的用户界面组件,因此,为维护这些核心组件包而投入资源已经不再像过去那样具有合理性了。最重要的是要确保设计系统、相关代币、开发规范以及测试流程得到妥善管理——正是这些因素才真正保证了设计的一致性。 作者:丹尼尔·柯蒂斯

阅读全文
技术实践

没有屏幕是否意味着数据质量更高?关于无屏幕可穿戴设备的基础知识

智能手表让人们更轻松地获取健康数据。只需快速看一下手腕,就能知道自己的心率、步数、睡眠时长,甚至还能估算出身体的恢复情况。 但显示这些信息仅仅是健康监测的一部分。更重要的是要准确且持续地收集这些数据,而且通常不能干扰人们的日常活动。 正是这一区别使得无屏幕的可穿戴设备得以进入消费市场。智能手环和无屏幕健身追踪器能够在不显示通知或不断要求用户查看手腕的情况下,记录各种健康数据和活动指标。它们会在后台收集信息,而最详细的数据分析结果则可以通过配套的智能手机应用程序获取。 然而,去掉屏幕并不会自动让可穿戴设备变得更准确。如果设备的传感器在应对运动、皮肤接触不良等因素时存在问题,即使它收集了数千次数据

阅读全文
技术实践

在你们解决数据问题之前,医疗领域的人工智能技术是无法正常运行的。

为临床环境开发人工智能其实比看起来要困难得多。这不仅仅意味着需要选择一个合适的模型或调整正确的参数。 当工程师们进入医院这个生态系统时,他们会很快发现:那些在其他行业中被他们广泛使用的工具,在这里往往无法正常使用。临床数据杂乱无章、极其敏感,而且分散在许多不同的系统中。妥善处理这些数据并非可有可无,而是整个系统能否正常运行的基础。 临床数据有多种形式。其中一部分是结构化的数据,比如存储在表格中的实验室检测结果;另一部分则是非结构化数据,比如医生手写的病历被扫描成PDF文件。所有这些数据都受到严格的隐私保护规定的约束,而且每一条数据都可能直接影响患者的诊疗效果。 与电子商务或广告领域不同,在这些

阅读全文
技术实践

光标技术利用S3 WAL机制,使Git存储系统的处理能力提升到每秒可接收超过300次推送请求。

Cursor推出了名为“Continuity”的Git存储架构,该架构以基于S3技术的预写日志作为数据存储的权威来源。这种设计将本地的NVMe仓库转换为缓存系统,并将副本协调机制与数据一致性保障机制分开处理。在模拟测试中,使用S3 Express One Zone时,Cursor能够实现线性扩展——当副本数量达到100个时,每秒仍可完成超过300次数据推送操作。 作者:Leela Kumili

阅读全文