Giới thiệu Recharts

Recharts là một thư viện vẽ biểu đồ (charting library) được xây dựng dựa trên D3.jsReact components. Nó cung cấp:
  • 📊 16+ loại biểu đồ - Line, Bar, Pie, Area, Radar, Scatter, Funnel…
  • ⚛️ React-native - Viết component thay vì configuration objects
  • 🎨 Highly Customizable - Dễ tùy chỉnh colors, fonts, animations
  • 📱 Responsive - Tự động responsive trên tất cả thiết bị
  • 🎯 Interactive - Tooltips, legends, crosshair, active dot…
  • 💾 Lightweight - ~60kb gzip
  • Accessible - Support ARIA labels

Cài đặt

# Cài đặt Recharts
npm install recharts

# Nếu dùng Next.js, không cần config đặc biệt
# Recharts support Server Components từ v2.10+

Dependencies

{
  "recharts": "^2.10.0",
  "react": "^18.0.0",
  "react-dom": "^18.0.0"
}

Các loại Biểu đồ (Chart Types)

LoạiMô tảUse Case
LineChartBiểu đồ đườngXu hướng theo thời gian
BarChartBiểu đồ cộtSo sánh giá trị
AreaChartBiểu đồ diện tíchXu hướng tích lũy
PieChartBiểu đồ trònPhần trăm, tỷ lệ
ScatterChartBiểu đồ phân tánTương quan 2 biến
RadarChartBiểu đồ radarSo sánh đa chiều
FunnelChartBiểu đồ ph漏斗Conversion funnel
SankeyChartSankey diagramFlow, alluvial
TreemapChartTreemapHierarchical data
ComposedChartMixed chartNhiều loại dữ liệu

Ví dụ Cơ Bản

1. Line Chart - Biểu đồ Đường

'use client'

import {
  LineChart,
  Line,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  Legend,
  ResponsiveContainer,
} from 'recharts'

const data = [
  { month: 'Jan', revenue: 4000, users: 2400 },
  { month: 'Feb', revenue: 3000, users: 1398 },
  { month: 'Mar', revenue: 2000, users: 9800 },
  { month: 'Apr', revenue: 2780, users: 3908 },
  { month: 'May', revenue: 1890, users: 4800 },
  { month: 'Jun', revenue: 2390, users: 3800 },
]

export function RevenueChart() {
  return (
    <ResponsiveContainer width="100%" height={300}>
      <LineChart data={data}>
        <CartesianGrid strokeDasharray="3 3" />
        <XAxis dataKey="month" />
        <YAxis />
        <Tooltip />
        <Legend />
        <Line
          type="monotone"
          dataKey="revenue"
          stroke="#8884d8"
          activeDot={{ r: 8 }}
        />
        <Line
          type="monotone"
          dataKey="users"
          stroke="#82ca9d"
          activeDot={{ r: 8 }}
        />
      </LineChart>
    </ResponsiveContainer>
  )
}

2. Bar Chart - Biểu đồ Cột

import {
  BarChart,
  Bar,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  Legend,
  ResponsiveContainer,
} from 'recharts'

const salesData = [
  { product: 'Product A', sales: 4000, revenue: 24000 },
  { product: 'Product B', sales: 3000, revenue: 21000 },
  { product: 'Product C', sales: 2000, revenue: 29000 },
  { product: 'Product D', sales: 2780, revenue: 39000 },
  { product: 'Product E', sales: 1890, revenue: 23000 },
]

export function SalesChart() {
  return (
    <ResponsiveContainer width="100%" height={300}>
      <BarChart data={salesData}>
        <CartesianGrid strokeDasharray="3 3" />
        <XAxis dataKey="product" />
        <YAxis />
        <Tooltip />
        <Legend />
        <Bar dataKey="sales" fill="#8884d8" />
        <Bar dataKey="revenue" fill="#82ca9d" />
      </BarChart>
    </ResponsiveContainer>
  )
}

3. Pie Chart - Biểu đồ Tròn

import { PieChart, Pie, Cell, Legend, Tooltip, ResponsiveContainer } from 'recharts'

const marketShare = [
  { name: 'Market A', value: 35 },
  { name: 'Market B', value: 25 },
  { name: 'Market C', value: 20 },
  { name: 'Market D', value: 15 },
  { name: 'Market E', value: 5 },
]

const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042', '#FF6B6B']

export function MarketShareChart() {
  return (
    <ResponsiveContainer width="100%" height={300}>
      <PieChart>
        <Pie
          data={marketShare}
          cx="50%"
          cy="50%"
          labelLine={false}
          label={({ name, value }) => `${name}: ${value}%`}
          outerRadius={80}
          fill="#8884d8"
          dataKey="value"
        >
          {marketShare.map((entry, index) => (
            <Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
          ))}
        </Pie>
        <Tooltip formatter={(value) => `${value}%`} />
        <Legend />
      </PieChart>
    </ResponsiveContainer>
  )
}

4. Area Chart - Biểu đồ Diện tích

import {
  AreaChart,
  Area,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  Legend,
  ResponsiveContainer,
} from 'recharts'

const trafficData = [
  { day: 'Mon', organic: 400, paid: 240, direct: 200 },
  { day: 'Tue', organic: 300, paid: 198, direct: 221 },
  { day: 'Wed', organic: 200, paid: 980, direct: 229 },
  { day: 'Thu', organic: 278, paid: 390, direct: 200 },
  { day: 'Fri', organic: 189, paid: 480, direct: 218 },
  { day: 'Sat', organic: 239, paid: 380, direct: 250 },
  { day: 'Sun', organic: 349, paid: 430, direct: 210 },
]

export function TrafficChart() {
  return (
    <ResponsiveContainer width="100%" height={300}>
      <AreaChart data={trafficData}>
        <defs>
          <linearGradient id="colorOrganic" x1="0" y1="0" x2="0" y2="1">
            <stop offset="5%" stopColor="#8884d8" stopOpacity={0.8} />
            <stop offset="95%" stopColor="#8884d8" stopOpacity={0} />
          </linearGradient>
          <linearGradient id="colorPaid" x1="0" y1="0" x2="0" y2="1">
            <stop offset="5%" stopColor="#82ca9d" stopOpacity={0.8} />
            <stop offset="95%" stopColor="#82ca9d" stopOpacity={0} />
          </linearGradient>
        </defs>
        <CartesianGrid strokeDasharray="3 3" />
        <XAxis dataKey="day" />
        <YAxis />
        <Tooltip />
        <Legend />
        <Area
          type="monotone"
          dataKey="organic"
          stroke="#8884d8"
          fillOpacity={1}
          fill="url(#colorOrganic)"
        />
        <Area
          type="monotone"
          dataKey="paid"
          stroke="#82ca9d"
          fillOpacity={1}
          fill="url(#colorPaid)"
        />
      </AreaChart>
    </ResponsiveContainer>
  )
}

5. Radar Chart - Biểu đồ Radar

import {
  RadarChart,
  PolarGrid,
  PolarAngleAxis,
  PolarRadiusAxis,
  Radar,
  Legend,
  Tooltip,
  ResponsiveContainer,
} from 'recharts'

const skillsData = [
  { skill: 'Frontend', value: 85 },
  { skill: 'Backend', value: 75 },
  { skill: 'Database', value: 80 },
  { skill: 'DevOps', value: 70 },
  { skill: 'UI/UX', value: 65 },
]

export function SkillsChart() {
  return (
    <ResponsiveContainer width="100%" height={300}>
      <RadarChart data={skillsData}>
        <PolarGrid />
        <PolarAngleAxis dataKey="skill" />
        <PolarRadiusAxis angle={90} domain={[0, 100]} />
        <Radar
          name="Skills"
          dataKey="value"
          stroke="#8884d8"
          fill="#8884d8"
          fillOpacity={0.6}
        />
        <Tooltip />
        <Legend />
      </RadarChart>
    </ResponsiveContainer>
  )
}

6. Composed Chart - Biểu đồ Hỗn hợp

import {
  ComposedChart,
  Bar,
  Line,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  Legend,
  ResponsiveContainer,
} from 'recharts'

const composedData = [
  { month: 'Jan', bar: 4000, line: 2400 },
  { month: 'Feb', bar: 3000, line: 1398 },
  { month: 'Mar', bar: 2000, line: 9800 },
  { month: 'Apr', bar: 2780, line: 3908 },
]

export function ComposedChartExample() {
  return (
    <ResponsiveContainer width="100%" height={300}>
      <ComposedChart data={composedData}>
        <CartesianGrid strokeDasharray="3 3" />
        <XAxis dataKey="month" />
        <YAxis />
        <Tooltip />
        <Legend />
        <Bar dataKey="bar" fill="#8884d8" />
        <Line type="monotone" dataKey="line" stroke="#ff7300" />
      </ComposedChart>
    </ResponsiveContainer>
  )
}

Components Chính

Chart Containers

// ResponsiveContainer - Responsive sizing
<ResponsiveContainer width="100%" height={300}>
  <LineChart data={data}>...</LineChart>
</ResponsiveContainer>

// Fixed size chart
<LineChart width={500} height={300} data={data}>
  ...
</LineChart>

Axis Components

// XAxis - Horizontal axis
<XAxis dataKey="month" />

// YAxis - Vertical axis
<YAxis yAxisId="left" />
<YAxis yAxisId="right" orientation="right" />

// PolarAngleAxis - Radar angle axis
<PolarAngleAxis dataKey="skill" />

// PolarRadiusAxis - Radar radius axis
<PolarRadiusAxis angle={90} domain={[0, 100]} />

Grid & Reference

// Grid background
<CartesianGrid strokeDasharray="3 3" />

// Reference line
<ReferenceLine y={8000} stroke="red" label="Target" />

// Reference area
<ReferenceArea x1="Feb" x2="Apr" fill="#ccc" />

Interactive Components

// Tooltip
<Tooltip
  contentStyle={{ backgroundColor: '#f0f0f0' }}
  formatter={(value) => `$${value}`}
  labelFormatter={(label) => `Month: ${label}`}
/>

// Legend
<Legend
  verticalAlign="bottom"
  height={36}
  wrapperStyle={{ paddingTop: '20px' }}
/>

// Brush - Range selector
<Brush dataKey="month" height={40} travellerWidth={8} />

Advanced Customization

Custom Tooltip

const CustomTooltip = ({ active, payload, label }: any) => {
  if (active && payload && payload.length) {
    return (
      <div className="bg-white p-3 border border-gray-300 rounded shadow">
        <p className="font-bold">{label}</p>
        {payload.map((entry: any, index: number) => (
          <p key={index} style={{ color: entry.color }}>
            {entry.name}: {entry.value}
          </p>
        ))}
      </div>
    )
  }
  return null
}

// Usage
<LineChart data={data}>
  <Tooltip content={<CustomTooltip />} />
</LineChart>

Custom Label

const renderCustomLabel = (props: any) => {
  const { x, y, value } = props
  return (
    <text
      x={x}
      y={y - 10}
      fill="#000"
      textAnchor="middle"
      className="text-xs font-bold"
    >
      {value}
    </text>
  )
}

// Usage
<Bar dataKey="sales" label={renderCustomLabel} />

Theme Customization

const COLORS = {
  primary: '#0088FE',
  secondary: '#00C49F',
  warning: '#FFBB28',
  danger: '#FF8042',
}

export function ThemedChart() {
  return (
    <LineChart data={data}>
      <Line
        dataKey="revenue"
        stroke={COLORS.primary}
        strokeWidth={2}
        dot={{ fill: COLORS.primary, r: 4 }}
        activeDot={{ r: 6 }}
      />
    </LineChart>
  )
}

Responsive Design

Mobile-friendly Charts

import { useState, useEffect } from 'react'

export function ResponsiveChart() {
  const [isMobile, setIsMobile] = useState(false)

  useEffect(() => {
    const handleResize = () => setIsMobile(window.innerWidth < 768)
    handleResize()
    window.addEventListener('resize', handleResize)
    return () => window.removeEventListener('resize', handleResize)
  }, [])

  return (
    <ResponsiveContainer width="100%" height={isMobile ? 250 : 400}>
      <LineChart data={data}>
        <XAxis
          dataKey="month"
          angle={isMobile ? -45 : 0}
          textAnchor={isMobile ? 'end' : 'middle'}
          height={isMobile ? 80 : 30}
        />
        <Tooltip />
        <Line dataKey="revenue" stroke="#8884d8" />
      </LineChart>
    </ResponsiveContainer>
  )
}

Performance Tips

1

Use ResponsiveContainer

Tự động responsive mà không cần media queries.
2

Lazy load charts

Dùng React.lazy + Suspense để lazy load charts.
3

Memo chart components

Wrap chart components với React.memo để tránh re-renders.
4

Optimize data

Chỉ pass cần thiết data vào chart component.
5

Debounce resize

Debounce window resize events để tránh quá nhiều re-renders.
6

Use server-side rendering

Recharts hỗ trợ SSR, tận dụng Next.js server components.

Real-world Example - Dashboard

'use client'

import { useState } from 'react'
import {
  LineChart,
  Line,
  BarChart,
  Bar,
  PieChart,
  Pie,
  Cell,
  ResponsiveContainer,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  Legend,
} from 'recharts'

const dashboardData = {
  revenue: [
    { month: 'Jan', value: 4000 },
    { month: 'Feb', value: 5000 },
    { month: 'Mar', value: 3000 },
  ],
  sales: [
    { product: 'A', sales: 400 },
    { product: 'B', sales: 300 },
    { product: 'C', sales: 200 },
  ],
  distribution: [
    { name: 'Web', value: 60 },
    { name: 'Mobile', value: 30 },
    { name: 'Other', value: 10 },
  ],
}

export function Dashboard() {
  return (
    <div className="grid grid-cols-1 md:grid-cols-2 gap-6 p-6">
      {/* Revenue Chart */}
      <div className="bg-white p-6 rounded-lg shadow">
        <h2 className="text-lg font-bold mb-4">Revenue Trend</h2>
        <ResponsiveContainer width="100%" height={250}>
          <LineChart data={dashboardData.revenue}>
            <CartesianGrid strokeDasharray="3 3" />
            <XAxis dataKey="month" />
            <YAxis />
            <Tooltip />
            <Line type="monotone" dataKey="value" stroke="#8884d8" />
          </LineChart>
        </ResponsiveContainer>
      </div>

      {/* Sales Chart */}
      <div className="bg-white p-6 rounded-lg shadow">
        <h2 className="text-lg font-bold mb-4">Sales by Product</h2>
        <ResponsiveContainer width="100%" height={250}>
          <BarChart data={dashboardData.sales}>
            <CartesianGrid strokeDasharray="3 3" />
            <XAxis dataKey="product" />
            <YAxis />
            <Tooltip />
            <Bar dataKey="sales" fill="#82ca9d" />
          </BarChart>
        </ResponsiveContainer>
      </div>

      {/* Distribution Chart */}
      <div className="bg-white p-6 rounded-lg shadow">
        <h2 className="text-lg font-bold mb-4">Channel Distribution</h2>
        <ResponsiveContainer width="100%" height={250}>
          <PieChart>
            <Pie
              data={dashboardData.distribution}
              cx="50%"
              cy="50%"
              labelLine={false}
              label={({ name, value }) => `${name}: ${value}%`}
              outerRadius={80}
              dataKey="value"
            >
              {dashboardData.distribution.map((entry, index) => (
                <Cell
                  key={`cell-${index}`}
                  fill={['#0088FE', '#00C49F', '#FFBB28'][index]}
                />
              ))}
            </Pie>
          </PieChart>
        </ResponsiveContainer>
      </div>
    </div>
  )
}

Alternatives Comparison

LibraryLoạiLearningBundle SizeFeatures
RechartsReactDễ~60kb16+ types
VictoryReactTrung bình~70kbComprehensive
NivoReactKhó~200kbVery rich
Chart.jsVanilla JSDễ~50kbStandard
D3.jsVanilla JSRất khó~150kbHighly flexible

Tài nguyên

Kết luận

Recharts là lựa chọn lý tưởng để thêm data visualization vào Next.js applications. Với 16+ loại biểu đồ, khả năng tùy chỉnh cao, responsive design, và bundle size nhỏ, nó giải quyết hầu hết các nhu cầu charting của bạn mà không cần phức tạp hóa. Sự kết hợp Recharts + shadcn/ui + TanStack Table tạo nên một stack hoàn hảo cho việc xây dựng professional dashboards và data-driven applications.