tricharts
v0.3.1
Published
Beautiful, interactive 3D charts for React — powered by Three.js and React Three Fiber
Maintainers
Readme
TriCharts
Beautiful, interactive 3D charts for React — powered by Three.js and React Three Fiber
Preview

This is a demo of the TriCharts library, showcasing a 3D bar chart rendering daily rainfall data for a year. With the days and months represented on the X and Z axes, and the rainfall amount on the Y axis, this chart provides a clear and interactive visualization of the data. The chart is fully interactive, allowing users to rotate, zoom, and click on individual bars to see detailed information.
Features
- 🚀 Performant - Built with Three.js and React Three Fiber for hardware-accelerated rendering
- 🔄 Interactive - Rotate, zoom, and click on chart elements
- 📱 Responsive - Automatically scales to fit any container size
- 🎨 Customizable - Multiple color schemes with easy styling options
- 🛠️ Simple API - Easy to use with sensible defaults
Installation
Install TriCharts along with required peer dependencies:
# Using npm
npm install tricharts react react-dom @react-three/fiber @react-three/drei three
# Using yarn
yarn add tricharts react react-dom @react-three/fiber @react-three/drei three
# Using pnpm
pnpm add tricharts react react-dom @react-three/fiber @react-three/drei threeRequirements: React 18+, @react-three/fiber 8+, @react-three/drei 9+, three 0.150+, and Node.js 18+.
Chart Types
TriCharts currently includes two chart types: Bar3DChart for 3D bar charts and Surface3DChart for continuous 3D surfaces.
Bar3DChart
A 3D bar chart is a chart that displays data as a series of bars, with the height of each bar representing the value of the data point.
Surface3DChart
A 3D surface chart is a chart that displays data as a surface, with the height of the surface representing the value of the data point.
Basic Usage
import { Bar3DChart, Surface3DChart } from "tricharts"
function App() {
return (
<>
<div style={{ height: "500px", width: "100%" }}>
<Bar3DChart data={[3, 5, 2, 7, 4]} />
</div>
<div style={{ height: "500px", width: "100%" }}>
<Surface3DChart
data={[
[3, 5, 4, 6],
[4, 6, 7, 5],
[2, 4, 6, 8],
[1, 3, 5, 7],
]}
xLabel="X"
yLabel="Height"
zLabel="Z"
/>
</div>
</>
)
}Using with Next.js App Router
When using TriCharts in Next.js App Router, you must add the "use client" directive at the top of your component file, as TriCharts requires client-side rendering:
"use client" // Add this directive at the top of your file
import { Bar3DChart, Surface3DChart } from "tricharts"
function App() {
return (
<>
<div style={{ height: "500px", width: "100%" }}>
<Bar3DChart data={[3, 5, 2, 7, 4]} />
</div>
<div style={{ height: "500px", width: "100%" }}>
<Surface3DChart
data={[
[3, 5, 4, 6],
[4, 6, 7, 5],
[2, 4, 6, 8],
[1, 3, 5, 7],
]}
/>
</div>
</>
)
}Responsive Layout
The chart automatically fills 100% of its parent container. Simply wrap it in a div with the desired dimensions:
<div style={{ height: "400px", width: "600px" }}>
<Bar3DChart data={data} />
</div>The camera automatically frames the chart and all of its labels, and refits when the container is resized or the data changes.
Controls
Each chart has a toolbar in the bottom-right corner to rotate, zoom and reset the view, so no gestures are needed. Pointing at a chart for the first time shows a short hint for the gestures.
| Action | Mouse / trackpad | Touch | Toolbar | | ---------- | ------------------------- | --------------- | ------- | | Rotate | Drag | Two-finger drag | ↺ ↻ | | Pan | Right-drag | — | | | Zoom | ⌘/Ctrl + scroll, or pinch | Pinch | − + | | Reset view | Double-click | Double-tap | ⛶ |
- Scrolling over a chart scrolls the page, so charts don't trap the scroll wheel. Hold ⌘ (Mac) or Ctrl while scrolling, or pinch on a trackpad, to zoom. Set
scrollZoom="always"to zoom on plain scrolling instead. - Swiping with one finger on a touch screen scrolls the page too; use two fingers to rotate and pinch to zoom. Set
touchRotate="one-finger"to rotate with one finger and pan with two instead, at the cost of page scrolling over the chart. - Zoom moves toward the cursor. Zooming out stops once the whole chart is in view, and zooming all the way out returns to the starting view.
- Rotation is limited to the front quarter of the chart, so the grid walls stay behind the data.
- Panning stays on the chart, so it can't be dragged out of view.
- Once you rotate, pan or zoom, the chart no longer refits on resize, until you reset the view or the data changes.
Hide the toolbar with showControls={false}, or move it with controlsPosition. Its colors can be themed with CSS custom properties on any ancestor: --tricharts-controls-bg, --tricharts-controls-border, --tricharts-controls-color, --tricharts-controls-hover and --tricharts-controls-focus.
Chart Examples
Simple Bar Chart
import { Bar3DChart } from "tricharts"
const data = [12, 19, 3, 5, 9, 3, 7]
function SingleRowBar() {
return (
<div style={{ height: "400px" }}>
<Bar3DChart
data={data}
colorScheme="random"
xLabel="Months"
xLabels={["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"]}
/>
</div>
)
}Multi-Series Bar Chart
import { Bar3DChart } from "tricharts"
const multiRowData = [
[12, 19, 3, 5, 2],
[22, 12, 15, 5, 9],
[8, 14, 12, 7, 11],
]
function MultiRowBar() {
return (
<div style={{ height: "500px" }}>
<Bar3DChart
data={multiRowData}
colorScheme="rainbow"
xLabel="Categories"
yLabel="Values"
zLabel="Year"
xLabels={["A", "B", "C", "D", "E"]}
zLabels={["2022", "2023", "2024"]}
gridSpacing={1.5}
/>
</div>
)
}Surface Chart
import { Surface3DChart } from "tricharts"
const surfaceData = [
[3, 5, 7, 6, 4],
[4, 7, 9, 8, 5],
[3, 6, 10, 7, 4],
[2, 4, 7, 5, 3],
]
function SurfaceExample() {
return (
<div style={{ height: "500px" }}>
<Surface3DChart
data={surfaceData}
colorScheme="rainbow"
xLabel="X Axis"
yLabel="Value"
zLabel="Z Axis"
xLabels={["A", "B", "C", "D", "E"]}
zLabels={["R1", "R2", "R3", "R4"]}
showSurfacePoints={true}
surfacePointRadius={0.12}
gridSpacing={0.8}
/>
</div>
)
}Interactive Bar Chart with Click Handler
import { useState } from "react"
import { Bar3DChart } from "tricharts"
function InteractiveChart() {
const [selectedBar, setSelectedBar] = useState(null)
const handleBarClick = (data) => {
setSelectedBar(data)
console.log(`Clicked on bar with value: ${data.value}`)
}
return (
<div style={{ height: "400px" }}>
<Bar3DChart
data={[12, 19, 3, 5, 2, 3]}
colorScheme="random"
onBarClick={handleBarClick}
/>
{selectedBar && (
<div>
Selected bar: {selectedBar.value} (index: {selectedBar.xIndex})
</div>
)}
</div>
)
}API Reference
Bar3DChart Props
| Prop Name | Type | Default | Description |
| ------------- | ------------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| data | number[] or number[][] | required | Data to visualize. Can be a 1D array for a single series or a 2D array for multiple series. |
| colorScheme | String | "blue" | Color scheme for bars. Options: "blue", "green", "red", "purple", "orange", "rainbow", "random" or a hex color string like "#ff5733". |
| gridSpacing | number | 1 | Spacing between bar footprints on the X and Z axes. |
| barSpacing | number | — | Deprecated. Use gridSpacing. Same behavior; kept for backward compatibility. |
| showGrid | boolean | true | Whether to show the grid lines. |
| showLabels | boolean | true | Whether to show value labels on top of each bar. |
| xLabel | string | undefined | Label for the X-axis. |
| yLabel | string | undefined | Label for the Y-axis. |
| zLabel | string | undefined | Label for the Z-axis. |
| xLabels | string[] | undefined | Labels for individual X-axis ticks. |
| zLabels | string[] | undefined | Labels for individual Z-axis ticks. |
| maxHeight | number | 10 | Maximum height of the tallest bar. Other bars are scaled proportionally. |
| onBarClick | function | undefined | Callback triggered when a bar is clicked. Receives an object with this shape: { value: number, xIndex: number, zIndex: number, xLabel: string, zLabel: string }. |
Surface3DChart Props
| Prop Name | Type | Default | Description |
| -------------------- | ------------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| data | number[] or number[][] | required | Data to visualize as a surface. Surface charts require at least a 2 x 2 grid of values. |
| colorScheme | String | "blue" | Base color or gradient palette for the surface. Supports named schemes, a hex color string, an array of color stops, or "random". |
| gridSpacing | number | 1 | Spacing between grid sample points on the X and Z axes. |
| barSpacing | number | — | Deprecated. Use gridSpacing. Same behavior; kept for backward compatibility. |
| showGrid | boolean | true | Whether to show the floor and vertical grid lines. |
| showLabels | boolean | true | Whether to show a numeric value label above each grid vertex on the surface (similar to value labels on Bar3DChart). |
| showWireframe | boolean | false | Whether to render the surface mesh in wireframe mode. |
| showSurfacePoints | boolean | true | Whether to render a small sphere at each data point on the surface. |
| surfacePointColor | string | undefined | Optional sphere color override. When omitted, the point spheres use the same height-based gradient as the surface. |
| surfacePointRadius | number | auto | Radius of the point spheres in world units. |
| xLabel | string | undefined | Label for the X-axis. |
| yLabel | string | undefined | Label for the Y-axis. |
| zLabel | string | undefined | Label for the Z-axis. |
| xLabels | string[] | undefined | Labels for individual X-axis ticks. |
| zLabels | string[] | undefined | Labels for individual Z-axis ticks. |
| maxHeight | number | 10 | Maximum height of the tallest point on the surface. Other values are scaled proportionally. |
| onBarClick | function | undefined | Callback triggered when the surface is clicked. Receives the nearest sampled point as { value, xIndex, zIndex, xLabel, zLabel }. |
Interaction Props
Both charts also accept these props:
| Prop Name | Type | Default | Description |
| ------------------ | -------------------------------------------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------- |
| showControls | boolean | true | Show the on-screen rotate, zoom and reset buttons. |
| controlsPosition | "top-left" | "top-right" | "bottom-left" | "bottom-right" | "bottom-right" | Corner of the chart the buttons appear in. |
| scrollZoom | "modifier" | "always" | "modifier" | "modifier": plain scrolling scrolls the page; ⌘/Ctrl + scroll or pinch zooms. "always": scrolling zooms. |
| touchRotate | "two-finger" | "one-finger" | "two-finger" | "two-finger": one-finger swipes scroll the page; two fingers rotate and pinch zooms. "one-finger": one finger rotates. |
Rendering Props
Charts only redraw while something is changing (the intro animation, orbiting, hovering), and stop rendering entirely while scrolled off-screen, so an idle chart costs nothing. A chart that mounts off-screen plays its intro when it scrolls into view.
| Prop Name | Type | Default | Description |
| ----------- | ----------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------- |
| frameloop | "demand" | "always" | "demand" | "demand": redraw only when something changes. "always": redraw every frame. |
| dpr | number | [number, number] | [1, 1.5] | Device pixel ratio, or a [min, max] range clamped to the screen's. The default keeps high-DPI phones from rendering at 3x. |
License
This project is licensed under the MIT License - see the LICENSE file for details.
