2

Candlestick Chart

PreviousNext

A financial chart component for visualizing OHLC (Open, High, Low, Close) price data with optional volume indicators.

"use client"

import { CandlestickChart } from "@/components/ui/charts"

// Stock price data for March-April
const chartData = [
  { date: "2024-03-01", open: 35, high: 45, low: 32, close: 44 },
  { date: "2024-03-04", open: 44, high: 42, low: 35, close: 38 },
  { date: "2024-03-05", open: 38, high: 40, low: 32, close: 37 },
  { date: "2024-03-06", open: 37, high: 25, low: 18, close: 20 },
  { date: "2024-03-07", open: 20, high: 22, low: 8, close: 10 },
  { date: "2024-03-08", open: 10, high: 22, low: 8, close: 20 },
  { date: "2024-03-11", open: 20, high: 22, low: 15, close: 18 },
  { date: "2024-03-12", open: 18, high: 45, low: 16, close: 41 },
  { date: "2024-03-13", open: 41, high: 56, low: 38, close: 54 },
  { date: "2024-03-14", open: 54, high: 42, low: 38, close: 40 },
  { date: "2024-03-15", open: 40, high: 68, low: 38, close: 65 },
  { date: "2024-03-18", open: 65, high: 68, low: 55, close: 66 },
  { date: "2024-03-19", open: 66, high: 45, low: 38, close: 40 },
  { date: "2024-03-20", open: 40, high: 42, low: 28, close: 37 },
  { date: "2024-03-21", open: 37, high: 35, low: 25, close: 30 },
  { date: "2024-03-22", open: 30, high: 32, low: 22, close: 25 },
  { date: "2024-03-25", open: 25, high: 28, low: 18, close: 22 },
  { date: "2024-03-26", open: 22, high: 25, low: 8, close: 10 },
  { date: "2024-03-27", open: 10, high: 22, low: 5, close: 17 },
  { date: "2024-03-28", open: 17, high: 25, low: 12, close: 23 },
  { date: "2024-03-29", open: 23, high: 35, low: 20, close: 32 },
  { date: "2024-04-01", open: 32, high: 48, low: 30, close: 46 },
  { date: "2024-04-02", open: 46, high: 42, low: 38, close: 40 },
  { date: "2024-04-03", open: 40, high: 50, low: 32, close: 48 },
  { date: "2024-04-04", open: 48, high: 50, low: 42, close: 46 },
  { date: "2024-04-05", open: 46, high: 60, low: 44, close: 58 },
  { date: "2024-04-08", open: 58, high: 62, low: 55, close: 60 },
  { date: "2024-04-09", open: 60, high: 50, low: 45, close: 47 },
  { date: "2024-04-10", open: 47, high: 45, low: 38, close: 40 },
  { date: "2024-04-11", open: 40, high: 42, low: 35, close: 38 },
  { date: "2024-04-12", open: 38, high: 40, low: 32, close: 35 },
  { date: "2024-04-15", open: 35, high: 38, low: 30, close: 34 },
  { date: "2024-04-16", open: 34, high: 38, low: 28, close: 33 },
  { date: "2024-04-17", open: 33, high: 35, low: 28, close: 30 },
  { date: "2024-04-18", open: 30, high: 35, low: 25, close: 28 },
  { date: "2024-04-19", open: 28, high: 25, low: 15, close: 18 },
  { date: "2024-04-22", open: 18, high: 22, low: 12, close: 20 },
  { date: "2024-04-23", open: 20, high: 15, low: 5, close: 8 },
  { date: "2024-04-24", open: 8, high: 18, low: 5, close: 15 },
  { date: "2024-04-25", open: 15, high: 32, low: 12, close: 28 },
  { date: "2024-04-26", open: 28, high: 30, low: 20, close: 22 },
  { date: "2024-04-29", open: 22, high: 35, low: 20, close: 32 },
  { date: "2024-04-30", open: 32, high: 58, low: 28, close: 55 },
  { date: "2024-05-01", open: 55, high: 58, low: 48, close: 51 },
  { date: "2024-05-02", open: 51, high: 75, low: 48, close: 72 },
  { date: "2024-05-03", open: 72, high: 85, low: 70, close: 83 },
  { date: "2024-05-06", open: 83, high: 78, low: 70, close: 73 },
  { date: "2024-05-07", open: 73, high: 80, low: 72, close: 75 },
]

export function CandlestickChartDemo() {
  return (
    <CandlestickChart
      data={chartData}
      upColor="#22c55e"
      downColor="#ef4444"
      showGrid
      aspectRatio={2.5}
    />
  )
}

About

The Candlestick Chart is a financial charting component used to display price movements over time. Each candlestick shows:

  • Open - The opening price for the period
  • High - The highest price during the period
  • Low - The lowest price during the period
  • Close - The closing price for the period
  • Color - Green for bullish (close > open), Red for bearish (close < open)

Installation

pnpm dlx shadcn@latest add https://ui.simplifying.ai/r/candlestick-chart.json

Usage

import { CandlestickChart } from "@/components/ui/charts"
 
const data = [
  { date: "2024-03-01", open: 100, high: 110, low: 95, close: 108 },
  { date: "2024-03-02", open: 108, high: 115, low: 105, close: 103 },
  { date: "2024-03-03", open: 103, high: 120, low: 100, close: 118 },
]
 
export function MyChart() {
  return <CandlestickChart data={data} />
}

Examples

Default

The default candlestick chart displays filled candles with green for bullish days and red for bearish days.

"use client"

import { CandlestickChart } from "@/components/ui/charts"

// Stock price data for March-April
const chartData = [
  { date: "2024-03-01", open: 35, high: 45, low: 32, close: 44 },
  { date: "2024-03-04", open: 44, high: 42, low: 35, close: 38 },
  { date: "2024-03-05", open: 38, high: 40, low: 32, close: 37 },
  { date: "2024-03-06", open: 37, high: 25, low: 18, close: 20 },
  { date: "2024-03-07", open: 20, high: 22, low: 8, close: 10 },
  { date: "2024-03-08", open: 10, high: 22, low: 8, close: 20 },
  { date: "2024-03-11", open: 20, high: 22, low: 15, close: 18 },
  { date: "2024-03-12", open: 18, high: 45, low: 16, close: 41 },
  { date: "2024-03-13", open: 41, high: 56, low: 38, close: 54 },
  { date: "2024-03-14", open: 54, high: 42, low: 38, close: 40 },
  { date: "2024-03-15", open: 40, high: 68, low: 38, close: 65 },
  { date: "2024-03-18", open: 65, high: 68, low: 55, close: 66 },
  { date: "2024-03-19", open: 66, high: 45, low: 38, close: 40 },
  { date: "2024-03-20", open: 40, high: 42, low: 28, close: 37 },
  { date: "2024-03-21", open: 37, high: 35, low: 25, close: 30 },
  { date: "2024-03-22", open: 30, high: 32, low: 22, close: 25 },
  { date: "2024-03-25", open: 25, high: 28, low: 18, close: 22 },
  { date: "2024-03-26", open: 22, high: 25, low: 8, close: 10 },
  { date: "2024-03-27", open: 10, high: 22, low: 5, close: 17 },
  { date: "2024-03-28", open: 17, high: 25, low: 12, close: 23 },
  { date: "2024-03-29", open: 23, high: 35, low: 20, close: 32 },
  { date: "2024-04-01", open: 32, high: 48, low: 30, close: 46 },
  { date: "2024-04-02", open: 46, high: 42, low: 38, close: 40 },
  { date: "2024-04-03", open: 40, high: 50, low: 32, close: 48 },
  { date: "2024-04-04", open: 48, high: 50, low: 42, close: 46 },
  { date: "2024-04-05", open: 46, high: 60, low: 44, close: 58 },
  { date: "2024-04-08", open: 58, high: 62, low: 55, close: 60 },
  { date: "2024-04-09", open: 60, high: 50, low: 45, close: 47 },
  { date: "2024-04-10", open: 47, high: 45, low: 38, close: 40 },
  { date: "2024-04-11", open: 40, high: 42, low: 35, close: 38 },
  { date: "2024-04-12", open: 38, high: 40, low: 32, close: 35 },
  { date: "2024-04-15", open: 35, high: 38, low: 30, close: 34 },
  { date: "2024-04-16", open: 34, high: 38, low: 28, close: 33 },
  { date: "2024-04-17", open: 33, high: 35, low: 28, close: 30 },
  { date: "2024-04-18", open: 30, high: 35, low: 25, close: 28 },
  { date: "2024-04-19", open: 28, high: 25, low: 15, close: 18 },
  { date: "2024-04-22", open: 18, high: 22, low: 12, close: 20 },
  { date: "2024-04-23", open: 20, high: 15, low: 5, close: 8 },
  { date: "2024-04-24", open: 8, high: 18, low: 5, close: 15 },
  { date: "2024-04-25", open: 15, high: 32, low: 12, close: 28 },
  { date: "2024-04-26", open: 28, high: 30, low: 20, close: 22 },
  { date: "2024-04-29", open: 22, high: 35, low: 20, close: 32 },
  { date: "2024-04-30", open: 32, high: 58, low: 28, close: 55 },
  { date: "2024-05-01", open: 55, high: 58, low: 48, close: 51 },
  { date: "2024-05-02", open: 51, high: 75, low: 48, close: 72 },
  { date: "2024-05-03", open: 72, high: 85, low: 70, close: 83 },
  { date: "2024-05-06", open: 83, high: 78, low: 70, close: 73 },
  { date: "2024-05-07", open: 73, high: 80, low: 72, close: 75 },
]

export function CandlestickChartDemo() {
  return (
    <CandlestickChart
      data={chartData}
      upColor="#22c55e"
      downColor="#ef4444"
      showGrid
      aspectRatio={2.5}
    />
  )
}

Hollow Candles

Traditional hollow candle style where bullish candles are hollow (outline only) and bearish candles are filled.

"use client"

import { CandlestickChart } from "@/components/ui/charts"

// Stock data for hollow candles demo
const chartData = [
  { date: "2024-03-01", open: 100, high: 108, low: 98, close: 105 },
  { date: "2024-03-04", open: 105, high: 112, low: 102, close: 98 },
  { date: "2024-03-05", open: 98, high: 102, low: 95, close: 100 },
  { date: "2024-03-06", open: 100, high: 115, low: 98, close: 112 },
  { date: "2024-03-07", open: 112, high: 118, low: 110, close: 108 },
  { date: "2024-03-08", open: 108, high: 115, low: 105, close: 113 },
  { date: "2024-03-11", open: 113, high: 120, low: 110, close: 118 },
  { date: "2024-03-12", open: 118, high: 125, low: 115, close: 110 },
  { date: "2024-03-13", open: 110, high: 115, low: 105, close: 112 },
  { date: "2024-03-14", open: 112, high: 128, low: 110, close: 125 },
  { date: "2024-03-15", open: 125, high: 132, low: 122, close: 118 },
  { date: "2024-03-18", open: 118, high: 125, low: 115, close: 122 },
  { date: "2024-03-19", open: 122, high: 135, low: 120, close: 132 },
  { date: "2024-03-20", open: 132, high: 140, low: 130, close: 128 },
  { date: "2024-03-21", open: 128, high: 135, low: 125, close: 133 },
  { date: "2024-03-22", open: 133, high: 142, low: 130, close: 138 },
]

export function CandlestickChartHollowDemo() {
  return (
    <CandlestickChart
      data={chartData}
      upColor="#22c55e"
      downColor="#ef4444"
      showGrid
      hollowCandles
      aspectRatio={2.5}
    />
  )
}
<CandlestickChart data={data} hollowCandles />

Scrollable (Large Dataset)

For large datasets with 100+ data points, use a scrollable layout where the y-axis stays fixed while the x-axis scrolls horizontally. This pattern shows the latest data first.

$30$40$50$60$70$80$90$100$110
JanFebMarApr

← Scroll horizontally to see more data →

"use client"

import * as React from "react"
import { scaleBand, scaleLinear } from "d3-scale"

import { cn } from "@/lib/utils"

interface CandlestickDataPoint {
  date: string
  open: number
  high: number
  low: number
  close: number
}

// Large dataset - 100+ trading days
const chartData: CandlestickDataPoint[] = [
  { date: "2024-01-02", open: 45, high: 52, low: 42, close: 50 },
  { date: "2024-01-03", open: 50, high: 55, low: 48, close: 48 },
  { date: "2024-01-04", open: 48, high: 50, low: 40, close: 42 },
  { date: "2024-01-05", open: 42, high: 48, low: 38, close: 46 },
  { date: "2024-01-08", open: 46, high: 52, low: 44, close: 50 },
  { date: "2024-01-09", open: 50, high: 58, low: 48, close: 56 },
  { date: "2024-01-10", open: 56, high: 62, low: 54, close: 58 },
  { date: "2024-01-11", open: 58, high: 60, low: 50, close: 52 },
  { date: "2024-01-12", open: 52, high: 56, low: 48, close: 54 },
  { date: "2024-01-16", open: 54, high: 60, low: 52, close: 58 },
  { date: "2024-01-17", open: 58, high: 65, low: 56, close: 62 },
  { date: "2024-01-18", open: 62, high: 68, low: 60, close: 55 },
  { date: "2024-01-19", open: 55, high: 58, low: 50, close: 52 },
  { date: "2024-01-22", open: 52, high: 55, low: 45, close: 48 },
  { date: "2024-01-23", open: 48, high: 52, low: 42, close: 50 },
  { date: "2024-01-24", open: 50, high: 58, low: 48, close: 56 },
  { date: "2024-01-25", open: 56, high: 62, low: 54, close: 60 },
  { date: "2024-01-26", open: 60, high: 68, low: 58, close: 65 },
  { date: "2024-01-29", open: 65, high: 72, low: 62, close: 70 },
  { date: "2024-01-30", open: 70, high: 75, low: 65, close: 68 },
  { date: "2024-01-31", open: 68, high: 72, low: 60, close: 62 },
  { date: "2024-02-01", open: 62, high: 65, low: 55, close: 58 },
  { date: "2024-02-02", open: 58, high: 62, low: 52, close: 60 },
  { date: "2024-02-05", open: 60, high: 68, low: 58, close: 65 },
  { date: "2024-02-06", open: 65, high: 72, low: 62, close: 70 },
  { date: "2024-02-07", open: 70, high: 78, low: 68, close: 75 },
  { date: "2024-02-08", open: 75, high: 80, low: 70, close: 72 },
  { date: "2024-02-09", open: 72, high: 75, low: 65, close: 68 },
  { date: "2024-02-12", open: 68, high: 72, low: 62, close: 70 },
  { date: "2024-02-13", open: 70, high: 78, low: 68, close: 76 },
  { date: "2024-02-14", open: 76, high: 82, low: 74, close: 80 },
  { date: "2024-02-15", open: 80, high: 85, low: 75, close: 78 },
  { date: "2024-02-16", open: 78, high: 82, low: 72, close: 75 },
  { date: "2024-02-20", open: 75, high: 80, low: 70, close: 78 },
  { date: "2024-02-21", open: 78, high: 85, low: 76, close: 82 },
  { date: "2024-02-22", open: 82, high: 88, low: 80, close: 72 },
  { date: "2024-02-23", open: 72, high: 75, low: 65, close: 68 },
  { date: "2024-02-26", open: 68, high: 72, low: 60, close: 65 },
  { date: "2024-02-27", open: 65, high: 70, low: 58, close: 62 },
  { date: "2024-02-28", open: 62, high: 68, low: 55, close: 58 },
  { date: "2024-02-29", open: 58, high: 62, low: 50, close: 55 },
  { date: "2024-03-01", open: 55, high: 60, low: 48, close: 52 },
  { date: "2024-03-04", open: 52, high: 58, low: 45, close: 56 },
  { date: "2024-03-05", open: 56, high: 65, low: 54, close: 62 },
  { date: "2024-03-06", open: 62, high: 70, low: 60, close: 68 },
  { date: "2024-03-07", open: 68, high: 75, low: 65, close: 72 },
  { date: "2024-03-08", open: 72, high: 78, low: 68, close: 65 },
  { date: "2024-03-11", open: 65, high: 70, low: 58, close: 60 },
  { date: "2024-03-12", open: 60, high: 65, low: 52, close: 58 },
  { date: "2024-03-13", open: 58, high: 65, low: 55, close: 62 },
  { date: "2024-03-14", open: 62, high: 70, low: 60, close: 68 },
  { date: "2024-03-15", open: 68, high: 75, low: 65, close: 55 },
  { date: "2024-03-18", open: 55, high: 58, low: 48, close: 50 },
  { date: "2024-03-19", open: 50, high: 55, low: 42, close: 52 },
  { date: "2024-03-20", open: 52, high: 60, low: 50, close: 58 },
  { date: "2024-03-21", open: 58, high: 65, low: 55, close: 62 },
  { date: "2024-03-22", open: 62, high: 68, low: 58, close: 65 },
  { date: "2024-03-25", open: 65, high: 72, low: 62, close: 70 },
  { date: "2024-03-26", open: 70, high: 78, low: 68, close: 75 },
  { date: "2024-03-27", open: 75, high: 82, low: 72, close: 80 },
  { date: "2024-03-28", open: 80, high: 85, low: 75, close: 78 },
  { date: "2024-04-01", open: 78, high: 82, low: 70, close: 72 },
  { date: "2024-04-02", open: 72, high: 75, low: 65, close: 68 },
  { date: "2024-04-03", open: 68, high: 72, low: 60, close: 70 },
  { date: "2024-04-04", open: 70, high: 78, low: 68, close: 76 },
  { date: "2024-04-05", open: 76, high: 82, low: 74, close: 80 },
  { date: "2024-04-08", open: 80, high: 88, low: 78, close: 85 },
  { date: "2024-04-09", open: 85, high: 92, low: 82, close: 88 },
  { date: "2024-04-10", open: 88, high: 95, low: 85, close: 78 },
  { date: "2024-04-11", open: 78, high: 82, low: 72, close: 75 },
  { date: "2024-04-12", open: 75, high: 80, low: 68, close: 72 },
  { date: "2024-04-15", open: 72, high: 78, low: 65, close: 76 },
  { date: "2024-04-16", open: 76, high: 85, low: 74, close: 82 },
  { date: "2024-04-17", open: 82, high: 90, low: 80, close: 88 },
  { date: "2024-04-18", open: 88, high: 95, low: 85, close: 92 },
  { date: "2024-04-19", open: 92, high: 98, low: 88, close: 85 },
  { date: "2024-04-22", open: 85, high: 88, low: 78, close: 80 },
  { date: "2024-04-23", open: 80, high: 85, low: 72, close: 82 },
  { date: "2024-04-24", open: 82, high: 90, low: 80, close: 88 },
  { date: "2024-04-25", open: 88, high: 95, low: 85, close: 92 },
  { date: "2024-04-26", open: 92, high: 100, low: 90, close: 98 },
]

const upColor = "#22c55e"
const downColor = "#ef4444"

export function CandlestickChartScrollableDemo() {
  const scrollRef = React.useRef<HTMLDivElement>(null)
  const [hoveredIndex, setHoveredIndex] = React.useState<number | null>(null)
  const [tooltipPos, setTooltipPos] = React.useState({ x: 0, y: 0 })

  // Fixed dimensions
  const height = 320
  const margin = { top: 20, right: 20, bottom: 40, left: 0 }
  const yAxisWidth = 50
  const candleWidth = 12
  const candleGap = 4
  const chartWidth = chartData.length * (candleWidth + candleGap)
  const innerHeight = height - margin.top - margin.bottom

  // Y Scale (price) - fixed based on all data
  const yScale = React.useMemo(() => {
    const allPrices = chartData.flatMap((d) => [d.high, d.low])
    const minPrice = Math.min(...allPrices)
    const maxPrice = Math.max(...allPrices)
    const padding = (maxPrice - minPrice) * 0.1
    return scaleLinear()
      .domain([Math.max(0, minPrice - padding), maxPrice + padding])
      .range([innerHeight, 0])
      .nice()
  }, [innerHeight])

  // X Scale
  const xScale = React.useMemo(() => {
    return scaleBand()
      .domain(chartData.map((d) => d.date))
      .range([0, chartWidth])
      .padding(0.3)
  }, [chartWidth])

  const ticks = yScale.ticks(6)

  // Get month labels
  const monthLabels = React.useMemo(() => {
    const labels: { date: string; x: number }[] = []
    let lastMonth = -1
    chartData.forEach((d) => {
      const date = new Date(d.date)
      const month = date.getMonth()
      if (month !== lastMonth) {
        labels.push({
          date: d.date,
          x: (xScale(d.date) ?? 0) + xScale.bandwidth() / 2,
        })
        lastMonth = month
      }
    })
    return labels
  }, [xScale])

  // Scroll to end on mount (show latest data)
  React.useEffect(() => {
    if (scrollRef.current) {
      scrollRef.current.scrollLeft = scrollRef.current.scrollWidth
    }
  }, [])

  return (
    <div className="w-full">
      <div className="flex">
        {/* Fixed Y-Axis */}
        <div className="flex-shrink-0" style={{ width: yAxisWidth }}>
          <svg width={yAxisWidth} height={height}>
            <g transform={`translate(0, ${margin.top})`}>
              {ticks.map((tick) => (
                <g key={tick}>
                  <text
                    x={yAxisWidth - 8}
                    y={yScale(tick)}
                    dy="0.32em"
                    textAnchor="end"
                    className="fill-muted-foreground text-xs"
                  >
                    ${tick}
                  </text>
                </g>
              ))}
            </g>
          </svg>
        </div>

        {/* Scrollable Chart Area */}
        <div
          ref={scrollRef}
          className="scrollbar-none flex-1 overflow-x-auto overflow-y-hidden"
          style={{
            height,
            scrollbarWidth: "none",
            msOverflowStyle: "none",
          }}
        >
          <div
            className="relative"
            style={{ width: chartWidth + margin.right, height }}
          >
            <svg
              width={chartWidth + margin.right}
              height={height}
              className="relative block"
            >
              <g transform={`translate(0, ${margin.top})`}>
                {/* Horizontal Grid Lines */}
                {ticks.map((tick) => (
                  <line
                    key={`grid-${tick}`}
                    x1={0}
                    x2={chartWidth}
                    y1={yScale(tick)}
                    y2={yScale(tick)}
                    stroke="hsl(var(--border))"
                    strokeDasharray="3 3"
                    strokeOpacity={0.5}
                  />
                ))}

                {/* Vertical Grid Lines */}
                {monthLabels.map((label, i) => (
                  <line
                    key={`v-${i}`}
                    x1={label.x}
                    x2={label.x}
                    y1={0}
                    y2={innerHeight}
                    stroke="hsl(var(--border))"
                    strokeDasharray="3 3"
                    strokeOpacity={0.5}
                  />
                ))}

                {/* Candlesticks */}
                {chartData.map((d, index) => {
                  const isUp = d.close >= d.open
                  const color = isUp ? upColor : downColor
                  const bodyTop = isUp ? d.close : d.open
                  const bodyBottom = isUp ? d.open : d.close
                  const x = (xScale(d.date) ?? 0) + xScale.bandwidth() / 2
                  const isHovered = hoveredIndex === index

                  return (
                    <g
                      key={index}
                      className={cn(
                        "cursor-pointer transition-opacity duration-150",
                        hoveredIndex !== null && !isHovered && "opacity-40"
                      )}
                      onMouseEnter={(e) => {
                        setHoveredIndex(index)
                        setTooltipPos({ x: e.clientX, y: e.clientY })
                      }}
                      onMouseMove={(e) => {
                        setTooltipPos({ x: e.clientX, y: e.clientY })
                      }}
                      onMouseLeave={() => setHoveredIndex(null)}
                    >
                      {/* Wick */}
                      <line
                        x1={x}
                        x2={x}
                        y1={yScale(d.high)}
                        y2={yScale(d.low)}
                        stroke={color}
                        strokeWidth={1}
                      />
                      {/* Body */}
                      <rect
                        x={x - candleWidth / 2}
                        y={yScale(bodyTop)}
                        width={candleWidth}
                        height={Math.max(
                          1,
                          yScale(bodyBottom) - yScale(bodyTop)
                        )}
                        fill={color}
                        stroke={color}
                        strokeWidth={1}
                        rx={1}
                      />
                    </g>
                  )
                })}

                {/* X Axis - Month labels */}
                <g transform={`translate(0, ${innerHeight})`}>
                  {monthLabels.map((label, i) => {
                    const date = new Date(label.date)
                    return (
                      <text
                        key={i}
                        x={label.x}
                        y={24}
                        textAnchor="middle"
                        className="fill-muted-foreground text-xs"
                      >
                        {date.toLocaleDateString("en-US", { month: "short" })}
                      </text>
                    )
                  })}
                </g>
              </g>
            </svg>
          </div>
        </div>
      </div>

      {/* Scroll hint */}
      <p className="text-muted-foreground mt-2 text-center text-xs">
        ← Scroll horizontally to see more data →
      </p>

      {/* Tooltip - Auto-adjusts position based on available space */}
      {hoveredIndex !== null &&
        (() => {
          const tooltipWidth = 140
          const tooltipHeight = 120
          const padding = 15

          // Check viewport bounds
          const viewportWidth =
            typeof window !== "undefined" ? window.innerWidth : 1000
          const viewportHeight =
            typeof window !== "undefined" ? window.innerHeight : 800

          const showOnLeft =
            tooltipPos.x + tooltipWidth + padding > viewportWidth
          const showBelow = tooltipPos.y - tooltipHeight - padding < 0

          return (
            <div
              className="pointer-events-none fixed z-50"
              style={{
                left: showOnLeft
                  ? tooltipPos.x - padding
                  : tooltipPos.x + padding,
                top: showBelow
                  ? tooltipPos.y + padding
                  : tooltipPos.y - padding,
                transform: `translate(${showOnLeft ? "-100%" : "0"}, ${showBelow ? "0" : "-100%"})`,
              }}
            >
              <div className="bg-background rounded-lg border px-3 py-2 shadow-lg">
                <p className="text-foreground mb-1 text-sm font-medium">
                  {new Date(chartData[hoveredIndex].date).toLocaleDateString(
                    "en-US",
                    { month: "short", day: "numeric", year: "numeric" }
                  )}
                </p>
                <div className="grid grid-cols-2 gap-x-4 gap-y-0.5 text-sm">
                  <span className="text-muted-foreground">Open</span>
                  <span
                    className="font-mono"
                    style={{
                      color:
                        chartData[hoveredIndex].close >=
                        chartData[hoveredIndex].open
                          ? upColor
                          : downColor,
                    }}
                  >
                    ${chartData[hoveredIndex].open}
                  </span>
                  <span className="text-muted-foreground">High</span>
                  <span
                    className="font-mono"
                    style={{
                      color:
                        chartData[hoveredIndex].close >=
                        chartData[hoveredIndex].open
                          ? upColor
                          : downColor,
                    }}
                  >
                    ${chartData[hoveredIndex].high}
                  </span>
                  <span className="text-muted-foreground">Low</span>
                  <span
                    className="font-mono"
                    style={{
                      color:
                        chartData[hoveredIndex].close >=
                        chartData[hoveredIndex].open
                          ? upColor
                          : downColor,
                    }}
                  >
                    ${chartData[hoveredIndex].low}
                  </span>
                  <span className="text-muted-foreground">Close</span>
                  <span
                    className="font-mono"
                    style={{
                      color:
                        chartData[hoveredIndex].close >=
                        chartData[hoveredIndex].open
                          ? upColor
                          : downColor,
                    }}
                  >
                    ${chartData[hoveredIndex].close}
                  </span>
                </div>
              </div>
            </div>
          )
        })()}

      {/* CSS for hiding scrollbar */}
      <style jsx>{`
        .scrollbar-none::-webkit-scrollbar {
          display: none;
        }
      `}</style>
    </div>
  )
}

This variant uses a custom implementation with:

  • Fixed y-axis on the left
  • Horizontally scrollable chart area
  • Auto-scrolls to show latest data on load
  • Fixed candle width for consistent display

API Reference

PropTypeDefaultDescription
dataCandlestickDataPoint[]requiredArray of OHLC data points
upColorstring"#22c55e"Color for bullish (up) candles
downColorstring"#ef4444"Color for bearish (down) candles
classNamestring-Additional CSS classes
showGridbooleantrueShow dashed grid lines
showTooltipbooleantrueEnable tooltip on hover
hollowCandlesbooleanfalseUse hollow style for bullish candles
aspectRationumber2.5Width to height ratio
valueFormatter(value: number) => string$XFormat price values
dateFormatter(date: Date) => stringMMMFormat date labels

Data Types

interface CandlestickDataPoint {
  date: string | Date // Trading date
  open: number // Opening price
  high: number // Highest price
  low: number // Lowest price
  close: number // Closing price
}

Customization

Custom Colors

Use different colors for bullish and bearish candles:

<CandlestickChart
  data={data}
  upColor="#3b82f6" // Blue for up
  downColor="#f97316" // Orange for down
/>

Date Formatting

Customize how dates appear on the x-axis:

<CandlestickChart
  data={data}
  dateFormatter={(date) =>
    date.toLocaleDateString("en-US", {
      month: "short",
      day: "numeric",
    })
  }
/>

Value Formatting

Format the price values in tooltips and y-axis:

<CandlestickChart
  data={data}
  valueFormatter={(value) => `€${value.toFixed(2)}`}
/>

Understanding Candlesticks

  • Green/Hollow candle: Bullish - price closed higher than it opened
  • Red/Filled candle: Bearish - price closed lower than it opened
  • Wick (thin line): Shows the high and low range
  • Body (thick rectangle): Shows the open and close range
  • Long wick, short body: High volatility, price reversal possible
  • Long body, short wick: Strong trend in that direction

Notes

  • Candlestick charts are the standard visualization for financial price data and technical analysis
  • Each candlestick represents a time period (day, hour, minute) depending on your data granularity
  • The hollow candles variant is preferred by many traders as it provides clearer visual distinction
  • For large datasets (100+ candles), use the scrollable variant to maintain readability
  • Color coding (green/red) helps quickly identify market trends and momentum
  • The component supports both Date objects and date strings for flexible data input
  • Consider adding volume bars below the candlestick chart for complete market analysis