React Charts with Recharts: Bar, Line and Pie Charts (2026)

Recharts is the most widely-used charting library for React — built on D3 under the hood, but with a declarative React API. It handles responsiveness, animations, tooltips and legends out of the box. This guide covers every major chart type with dark-theme styling and production-ready patterns.

Setup and ResponsiveContainer

npm install recharts
import { ResponsiveContainer, BarChart, Bar } from 'recharts'

// Always wrap in ResponsiveContainer for fluid width
function MyChart() {
  return (
    <ResponsiveContainer width="100%" height={300}>
      <BarChart data={data}>
        <Bar dataKey="value" />
      </BarChart>
    </ResponsiveContainer>
  )
}

BarChart

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

const revenueData = [
  { month: 'Jan', revenue: 42000, expenses: 28000 },
  { month: 'Feb', revenue: 53000, expenses: 31000 },
  { month: 'Mar', revenue: 48000, expenses: 29000 },
  { month: 'Apr', revenue: 61000, expenses: 35000 },
  { month: 'May', revenue: 55000, expenses: 32000 },
  { month: 'Jun', revenue: 67000, expenses: 38000 },
]

function RevenueChart() {
  return (
    <ResponsiveContainer width="100%" height={300}>
      <BarChart data={revenueData} margin={{ top: 5, right: 30, left: 20, bottom: 5 }}>
        <CartesianGrid strokeDasharray="3 3" stroke="rgba(99,102,241,0.15)" />
        <XAxis dataKey="month" stroke="#64748b" tick={{ fill: '#94a3b8' }} />
        <YAxis stroke="#64748b" tick={{ fill: '#94a3b8' }} tickFormatter={v => `$${v/1000}k`} />
        <Tooltip content={<CustomTooltip />} />
        <Legend />
        <Bar dataKey="revenue" fill="#6366f1" radius={[4, 4, 0, 0]} name="Revenue" />
        <Bar dataKey="expenses" fill="#22d3ee" radius={[4, 4, 0, 0]} name="Expenses" />
      </BarChart>
    </ResponsiveContainer>
  )
}

// Stacked bar chart
<BarChart data={data}>
  <Bar dataKey="desktop" stackId="a" fill="#6366f1" />
  <Bar dataKey="mobile" stackId="a" fill="#22d3ee" radius={[4, 4, 0, 0]} />
</BarChart>

// Bar with individual cell colors
<Bar dataKey="value">
  {data.map((entry, index) => (
    <Cell key={index} fill={entry.value > 0 ? '#22c55e' : '#ef4444'} />
  ))}
</Bar>

LineChart and AreaChart

import { LineChart, Line, AreaChart, Area } from 'recharts'

const trafficData = [
  { date: 'Mon', pageviews: 1200, sessions: 800 },
  { date: 'Tue', pageviews: 1900, sessions: 1100 },
  { date: 'Wed', pageviews: 1600, sessions: 950 },
  { date: 'Thu', pageviews: 2100, sessions: 1400 },
  { date: 'Fri', pageviews: 1800, sessions: 1200 },
  { date: 'Sat', pageviews: 900, sessions: 600 },
  { date: 'Sun', pageviews: 750, sessions: 500 },
]

function TrafficLineChart() {
  return (
    <ResponsiveContainer width="100%" height={300}>
      <LineChart data={trafficData}>
        <CartesianGrid strokeDasharray="3 3" stroke="rgba(99,102,241,0.15)" />
        <XAxis dataKey="date" stroke="#64748b" tick={{ fill: '#94a3b8' }} />
        <YAxis stroke="#64748b" tick={{ fill: '#94a3b8' }} />
        <Tooltip content={<CustomTooltip />} />
        <Legend />
        <Line
          type="monotone"
          dataKey="pageviews"
          stroke="#6366f1"
          strokeWidth={2}
          dot={{ r: 4, fill: '#6366f1' }}
          activeDot={{ r: 6 }}
          name="Page Views"
        />
        <Line
          type="monotone"
          dataKey="sessions"
          stroke="#22d3ee"
          strokeWidth={2}
          dot={false}
          name="Sessions"
        />
      </LineChart>
    </ResponsiveContainer>
  )
}

// Gradient area chart
function GradientAreaChart() {
  return (
    <ResponsiveContainer width="100%" height={300}>
      <AreaChart data={trafficData}>
        <defs>
          <linearGradient id="colorPageviews" x1="0" y1="0" x2="0" y2="1">
            <stop offset="5%" stopColor="#6366f1" stopOpacity={0.3} />
            <stop offset="95%" stopColor="#6366f1" stopOpacity={0} />
          </linearGradient>
        </defs>
        <CartesianGrid strokeDasharray="3 3" stroke="rgba(99,102,241,0.15)" />
        <XAxis dataKey="date" tick={{ fill: '#94a3b8' }} />
        <YAxis tick={{ fill: '#94a3b8' }} />
        <Tooltip />
        <Area
          type="monotone"
          dataKey="pageviews"
          stroke="#6366f1"
          strokeWidth={2}
          fill="url(#colorPageviews)"
        />
      </AreaChart>
    </ResponsiveContainer>
  )
}

PieChart and DonutChart

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

const browserData = [
  { name: 'Chrome', value: 65 },
  { name: 'Safari', value: 18 },
  { name: 'Firefox', value: 10 },
  { name: 'Edge', value: 7 },
]

const COLORS = ['#6366f1', '#22d3ee', '#f59e0b', '#10b981']

function BrowserPieChart() {
  return (
    <ResponsiveContainer width="100%" height={300}>
      <PieChart>
        <Pie
          data={browserData}
          cx="50%"
          cy="50%"
          outerRadius={100}
          dataKey="value"
          label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}
          labelLine={true}
        >
          {browserData.map((_, index) => (
            <Cell key={index} fill={COLORS[index % COLORS.length]} />
          ))}
        </Pie>
        <Tooltip formatter={(value) => [`${value}%`, 'Share']} />
      </PieChart>
    </ResponsiveContainer>
  )
}

// Donut chart — add innerRadius
<Pie
  data={browserData}
  cx="50%"
  cy="50%"
  innerRadius={60}     // Creates donut hole
  outerRadius={100}
  dataKey="value"
  paddingAngle={3}     // Gap between segments
>

ComposedChart

import { ComposedChart, Bar, Line, Area } from 'recharts'

// Mix chart types in one chart
function SalesComposedChart() {
  return (
    <ResponsiveContainer width="100%" height={300}>
      <ComposedChart data={data}>
        <CartesianGrid strokeDasharray="3 3" stroke="rgba(99,102,241,0.15)" />
        <XAxis dataKey="month" tick={{ fill: '#94a3b8' }} />
        <YAxis yAxisId="left" tick={{ fill: '#94a3b8' }} />
        <YAxis yAxisId="right" orientation="right" tick={{ fill: '#94a3b8' }} />
        <Tooltip />
        <Legend />
        <Bar yAxisId="left" dataKey="orders" fill="#6366f1" radius={[4,4,0,0]} />
        <Line yAxisId="right" type="monotone" dataKey="revenue" stroke="#22d3ee" strokeWidth={2} />
        <Area yAxisId="left" type="monotone" dataKey="target" fill="rgba(34,211,238,0.1)" stroke="#22d3ee" strokeDasharray="5 5" />
      </ComposedChart>
    </ResponsiveContainer>
  )
}

Custom Tooltip

interface TooltipProps {
  active?: boolean
  payload?: Array<{ name: string; value: number; color: string }>
  label?: string
}

function CustomTooltip({ active, payload, label }: TooltipProps) {
  if (!active || !payload?.length) return null

  return (
    <div style={{
      background: '#0d1424',
      border: '1px solid rgba(99,102,241,0.3)',
      borderRadius: 8,
      padding: '12px 16px',
    }}>
      <p style={{ color: '#94a3b8', marginBottom: 8, fontSize: 12 }}>{label}</p>
      {payload.map((entry) => (
        <div key={entry.name} style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
          <span style={{ width: 8, height: 8, borderRadius: '50%', background: entry.color, display: 'inline-block' }} />
          <span style={{ color: '#e2e8f0', fontSize: 13 }}>
            {entry.name}: <strong>{entry.value.toLocaleString()}</strong>
          </span>
        </div>
      ))}
    </div>
  )
}

Real-Time Data

function RealTimeChart() {
  const [data, setData] = useState(() =>
    Array.from({ length: 20 }, (_, i) => ({ time: i, value: Math.random() * 100 }))
  )

  useEffect(() => {
    const interval = setInterval(() => {
      setData(prev => [
        ...prev.slice(1),   // Remove oldest point
        { time: prev[prev.length - 1].time + 1, value: Math.random() * 100 }
      ])
    }, 1000)
    return () => clearInterval(interval)
  }, [])

  return (
    <ResponsiveContainer width="100%" height={200}>
      <LineChart data={data}>
        <Line
          type="monotone"
          dataKey="value"
          stroke="#6366f1"
          dot={false}
          isAnimationActive={false}  // Disable animation for real-time — prevents flicker
        />
        <YAxis domain={[0, 100]} tick={{ fill: '#94a3b8' }} />
        <Tooltip />
      </LineChart>
    </ResponsiveContainer>
  )
}

Dark Theme Styling

// Global dark theme defaults — apply to all chart components
const darkTheme = {
  cartesianGrid: { stroke: 'rgba(99,102,241,0.15)', strokeDasharray: '3 3' },
  axis: { stroke: '#1e293b', tick: { fill: '#64748b', fontSize: 12 } },
  tooltip: {
    contentStyle: {
      background: '#0d1424',
      border: '1px solid rgba(99,102,241,0.3)',
      borderRadius: 8,
      color: '#e2e8f0',
    },
    labelStyle: { color: '#94a3b8' },
    cursor: { fill: 'rgba(99,102,241,0.08)' },
  },
  legend: { wrapperStyle: { color: '#94a3b8', fontSize: 13 } },
}

// Usage
<Tooltip
  contentStyle={darkTheme.tooltip.contentStyle}
  labelStyle={darkTheme.tooltip.labelStyle}
  cursor={darkTheme.tooltip.cursor}
/>