The timeseries chart is a specialized chart for displaying time-based data.
Each data point is a tuple of [timestamp_in_ms, value].
Basic Line Chart
A simple line chart displaying multiple data series over time.
import { ChartPalette, TimeseriesChart } from "@cloudflare/kumo";
import * as echarts from "echarts/core";
import { useMemo } from "react";
/**
* Basic line chart example showing simple time-based data visualization.
*/
export function BasicLineChartDemo() {
const isDarkMode = useIsDarkMode();
const data = useMemo(
() => [
{
name: "Requests",
data: buildSeriesData(0, 50, 60_000, 1),
color: ChartPalette.semantic("Neutral", isDarkMode),
},
{
name: "Errors",
data: buildSeriesData(1, 50, 60_000, 0.3),
color: ChartPalette.semantic("Attention", isDarkMode),
},
],
[isDarkMode],
);
return (
<TimeseriesChart
echarts={echarts}
isDarkMode={isDarkMode}
data={data}
xAxisName="Time (UTC)"
yAxisName="Count"
/>
);
} Custom X-Axis Label Format
Use the xAxisTickLabelFormat prop to control how x-axis tick
labels are rendered. The formatter receives the raw timestamp in milliseconds
and returns a display string, overriding EChartsβ built-in time formatting.
import { ChartPalette, TimeseriesChart } from "@cloudflare/kumo";
import * as echarts from "echarts/core";
import { useMemo } from "react";
/**
* Timeseries chart with custom axis tick label formats for both x-axis (HH:MM) and y-axis (compact numbers).
*/
export function CustomAxisLabelFormatDemo() {
const isDarkMode = useIsDarkMode();
const data = useMemo(
() => [
{
name: "Requests",
data: buildSeriesData(0, 50, 60_000, 1000),
color: ChartPalette.semantic("Neutral", isDarkMode),
},
],
[isDarkMode],
);
return (
<TimeseriesChart
echarts={echarts}
isDarkMode={isDarkMode}
data={data}
xAxisName="Time (UTC)"
yAxisName="Requests"
xAxisTickFormat={(ts) => {
const d = new Date(ts);
return `${d.getHours().toString().padStart(2, "0")}:${d.getMinutes().toString().padStart(2, "0")}`;
}}
yAxisTickFormat={(value) => {
if (value >= 1000) return `${value / 1000}k`;
return value.toString();
}}
tooltipValueFormat={(value) => `${(value / 1000).toFixed(1)}k requests`}
/>
);
} Gradient Fill
Set gradient to true to render a vertical gradient
fill beneath each line series. The fill fades from the series color at the top
to transparent at the bottom, giving the chart a polished area-chart look
without losing the clarity of individual lines.
import { ChartPalette, TimeseriesChart } from "@cloudflare/kumo";
import * as echarts from "echarts/core";
import { useMemo } from "react";
/**
* Timeseries chart with gradient fill beneath each line series.
*/
export function GradientLineChartDemo() {
const isDarkMode = useIsDarkMode();
const data = useMemo(
() => [
{
name: "Requests",
data: buildSeriesData(0, 50, 60_000, 1),
color: ChartPalette.semantic("Neutral", isDarkMode),
},
{
name: "Errors",
data: buildSeriesData(1, 50, 60_000, 0.3),
color: ChartPalette.semantic("Attention", isDarkMode),
},
],
[isDarkMode],
);
return (
<TimeseriesChart
echarts={echarts}
isDarkMode={isDarkMode}
data={data}
xAxisName="Time (UTC)"
yAxisName="Count"
gradient
/>
);
} Incomplete Data
Use the incomplete prop to indicate regions where data may be
incomplete or still being collected.
import { ChartPalette, TimeseriesChart } from "@cloudflare/kumo";
import * as echarts from "echarts/core";
import { useMemo } from "react";
/**
* Timeseries chart with incomplete data regions highlighted.
*/
export function IncompleteDataChartDemo() {
const isDarkMode = useIsDarkMode();
const data = useMemo(
() => [
{
name: "Bandwidth",
data: buildSeriesData(0, 50, 60_000, 1),
color: ChartPalette.color(0, isDarkMode),
},
],
[isDarkMode],
);
const incompleteTimestamp = data[0].data[data[0].data.length - 5][0];
return (
<TimeseriesChart
echarts={echarts}
isDarkMode={isDarkMode}
data={data}
xAxisName="Time (UTC)"
yAxisName="Mbps"
incomplete={{ after: incompleteTimestamp }}
/>
);
} Time Range Selection
Enable time range selection by providing the onTimeRangeChange
callback. Users can click and drag on the chart to select a time range.
import { ChartPalette, TimeseriesChart } from "@cloudflare/kumo";
import * as echarts from "echarts/core";
import { useMemo } from "react";
/**
* Timeseries chart with time range selection enabled.
*/
export function TimeRangeSelectionChartDemo() {
const isDarkMode = useIsDarkMode();
const data = useMemo(
() => [
{
name: "CPU Usage",
data: buildSeriesData(0, 50, 60_000, 1),
color: ChartPalette.color(0, isDarkMode),
},
],
[isDarkMode],
);
return (
<TimeseriesChart
echarts={echarts}
isDarkMode={isDarkMode}
data={data}
xAxisName="Time (UTC)"
yAxisName="%"
onTimeRangeChange={(from, to) => {
alert(
`Selected range:\nFrom: ${new Date(from).toLocaleString()}\nTo: ${new Date(to).toLocaleString()}`,
);
}}
/>
);
} Bar Chart
Set type=βbarβ to render series as stacked bars instead of lines.
All other props β axes, tooltips, colors β work identically.
import { ChartPalette, TimeseriesChart } from "@cloudflare/kumo";
import * as echarts from "echarts/core";
import { useMemo } from "react";
/**
* Timeseries chart rendered as a stacked bar chart.
*/
export function BarChartDemo() {
const isDarkMode = useIsDarkMode();
const data = useMemo(
() => [
{
name: "Requests",
data: buildSeriesData(0, 20, 3_600_000, 1),
color: ChartPalette.semantic("Neutral", isDarkMode),
},
{
name: "Errors",
data: buildSeriesData(1, 20, 3_600_000, 0.3),
color: ChartPalette.semantic("Attention", isDarkMode),
},
],
[isDarkMode],
);
return (
<TimeseriesChart
echarts={echarts}
isDarkMode={isDarkMode}
type="bar"
data={data}
xAxisName="Time (UTC)"
yAxisName="Count"
/>
);
} Legend Highlight
Hovering a legend item highlights the corresponding series on the chart and
fades the others. Use onPointerEnter and
onPointerLeave on ChartLegend items together with
dispatchAction on the chart ref.
import { ChartPalette, TimeseriesChart, ChartLegend, LayerCard } from "@cloudflare/kumo";
import * as echarts from "echarts/core";
import { useMemo, useRef, useState } from "react";
/**
* Timeseries chart with legend items that highlight the corresponding series on hover.
* Hovering a legend item dispatches a highlight action to the chart and fades the other legend items.
*/
export function LegendHighlightDemo() {
const isDarkMode = useIsDarkMode();
const chartRef = useRef<echarts.ECharts>(null);
const [hoveredSeries, setHoveredSeries] = useState<string | null>(null);
const series = useMemo(
() => [
{
name: "P99",
color: ChartPalette.semantic("Attention", isDarkMode),
value: "124",
unit: "ms",
},
{
name: "P95",
color: ChartPalette.semantic("Warning", isDarkMode),
value: "76",
unit: "ms",
},
{
name: "P75",
color: ChartPalette.semantic("Neutral", isDarkMode),
value: "32",
unit: "ms",
},
{
name: "P50",
color: ChartPalette.semantic("NeutralLight", isDarkMode),
value: "10",
unit: "ms",
},
],
[isDarkMode],
);
const data = useMemo(
() =>
series.map((s, i) => ({
name: s.name,
data: buildSeriesData(3 - i, 30, 60_000, 1 - i * 0.2),
color: s.color,
})),
[series],
);
return (
<LayerCard>
<LayerCard.Secondary>Read latency</LayerCard.Secondary>
<LayerCard.Primary>
<div className="flex divide-x divide-kumo-line gap-4 px-2 mb-2">
{series.map((s) => (
<ChartLegend.LargeItem
key={s.name}
name={s.name}
color={s.color}
value={s.value}
unit={s.unit}
inactive={hoveredSeries !== null && hoveredSeries !== s.name}
onPointerEnter={() => {
setHoveredSeries(s.name);
chartRef.current?.dispatchAction({
type: "highlight",
seriesName: s.name,
});
}}
onPointerLeave={() => {
setHoveredSeries(null);
chartRef.current?.dispatchAction({
type: "downplay",
seriesName: s.name,
});
}}
/>
))}
</div>
<TimeseriesChart
ref={chartRef}
xAxisName="Time (UTC)"
echarts={echarts}
isDarkMode={isDarkMode}
data={data}
height={300}
/>
</LayerCard.Primary>
</LayerCard>
);
} Loading State
Set loading to true to display an animated sine-wave
skeleton while data is being fetched. The chart canvas is hidden until loading
completes; swap back to loading={false} to reveal the chart.
import { TimeseriesChart } from "@cloudflare/kumo";
import * as echarts from "echarts/core";
/**
* Timeseries chart in loading state, showing the animated sine-wave skeleton.
* Loads for 5 seconds then reveals the real chart. A button restarts the cycle.
*/
export function LoadingChartDemo() {
const isDarkMode = useIsDarkMode();
return (
<div className="flex flex-col flex-1 w-full">
<TimeseriesChart
echarts={echarts}
isDarkMode={isDarkMode}
xAxisName="Time (UTC)"
yAxisName="Count"
data={[]}
loading
/>
</div>
);
}