Vercel wordmark
Tremor is joining Vercel.
NewBuild faster with 300+ Premium Blocks

React components to build dashboards

35+ fully open-source, accessible components for dashboards and charts. Built with React, Tailwind CSS and Radix UI.

Built forReactTailwind CSS

Portfolio Value

$25.00

Beautiful defaults, and simple props for every component

We already pushed the pixels so that you can focus on data. Customize quickly, spend less time on design.

Online payments

  • Successful
    263
  • Refunded
    18
  • Fraudulent
    9

Accessible by design

Built on Recharts and Radix UI, Tremor provides the essentials for production-ready UI.

55 – 75

Build the most complex filters

Modular input components for better interaction with your data, with full support for keyboard navigation.

example.com

99.9% uptime

Advanced visualizations

Tracker, Bar Lists, and many more components to visualize complex use cases gracefully.

Customer voices on X (formely twitter)

Leading developers build with Tremor

  • Tremor makes it so easy to build beautiful dashboards. The ReactJS / Next.js ecosystem just keeps getting better and better.

    X (formerly Twitter) profile picture of user Guillermo Rauch
    CEO @ Vercel
  • tremor.so is dope

    X (formerly Twitter) profile picture of user Peer Richelsen
    Co-Founder @ Cal.com
  • Made quite some progress in the last week. Huge thanks @tremorlabs for saving me weeks of creating custom components.

    X (formerly Twitter) profile picture of user Eyk Rehbein
    Senior Software Engineer @ Sonic
  • Love how clean the dashboard library from @tremorlabs is.

    X (formerly Twitter) profile picture of user Anthony Morris
    Senior Software Engineer @ Stripe
  • We've loved working with the @tremorlabs team and contributing to the project. It's moving fast and they're releasing really meaningful changes. We expect to continue working with Tremor on additional starter kits like this one.

    X (formerly Twitter) profile picture of user Tinybird
    Data platform provider
  • Creating dashboards quickly with @tremorlabs

    X (formerly Twitter) profile picture of user Drew Bredvick
    Sales Engineering Manager @ Vercel
  • I am in love with the @tremorlabs viz library & we've been working to adopt it in our Starter Kits.

    X (formerly Twitter) profile picture of user Alasdair Brown
    DevRel Lead @ Tinybird
  • Tremor looks interesting. Cool React components to build dashboards tremor.so

    X (formerly Twitter) profile picture of user Flavio Copes
    Creator of Bootcamp.dev
  • Fantastic set of UI modular components @tremorlabs to build dashboards with React. It comes with pre-built modular KPI cards.

    X (formerly Twitter) profile picture of user Andrej Baranovskij
    Founder @ Katanaml.io
  • Super excited to see the Tremor release, Looking forward to using it in my side projects and maybe internally at Mentimeter :) it looks fantastic

    X (formerly Twitter) profile picture of user Geordi Dearns
    Engineering Manager @ Mentimeter

Get started in seconds

Copy-and-paste or NPM package? We have it.

// Tremor DonutChart [v0.0.0] import React from "react"import { Pie, PieChart as ReChartsDonutChart, ResponsiveContainer, Sector, Tooltip,} from "recharts" import { AvailableChartColors, AvailableChartColorsKeys, constructCategoryColors, getColorClassName,} from "@/lib/chartUtils"import { cx } from "@/lib/utils" const sumNumericArray = (arr: number[]): number => arr.reduce((sum, num) => sum + num, 0) const parseData = ( data: Record<string, any>[], categoryColors: Map<string, AvailableChartColorsKeys>, category: string,) => data.map((dataPoint) => ({ ...dataPoint, color: categoryColors.get(dataPoint[category]) || AvailableChartColors[0], className: getColorClassName( categoryColors.get(dataPoint[category]) || AvailableChartColors[0], "fill", ), })) const calculateDefaultLabel = (data: any[], valueKey: string): number => sumNumericArray(data.map((dataPoint) => dataPoint[valueKey])) const parseLabelInput = ( labelInput: string | undefined, valueFormatter: (value: number) => string, data: any[], valueKey: string,): string => labelInput || valueFormatter(calculateDefaultLabel(data, valueKey)) //#region Tooltip type TooltipProps = Pick<ChartTooltipProps, "active" | "payload"> type PayloadItem = { category: string value: number color: AvailableChartColorsKeys} interface ChartTooltipProps { active: boolean | undefined payload: PayloadItem[] valueFormatter: (value: number) => string} const ChartTooltip = ({ active, payload, valueFormatter,}: ChartTooltipProps) => { if (active && payload && payload.length) { return ( <div className={cx( // base "rounded-md border text-sm shadow-md", // border color "border-gray-200 dark:border-gray-800", // background color "bg-white dark:bg-gray-950", )} > <div className={cx("space-y-1 px-4 py-2")}> {payload.map(({ value, category, color }, index) => ( <div key={`id-${index}`} className="flex items-center justify-between space-x-8" > <div className="flex items-center space-x-2"> <span aria-hidden="true" className={cx( "size-2 shrink-0 rounded-full", getColorClassName(color, "bg"), )} /> <p className={cx( // base "whitespace-nowrap text-right", // text col dark:text-gray-500or "text-gray-700 dark:text-gray-300", )} > {category} </p> </div> <p className={cx( // base "whitespace-nowrap text-right font-medium tabular-nums", // text color "text-gray-900 dark:text-gray-50", )} > {valueFormatter(value)} </p> </div> ))} </div> </div> ) } return null} const renderInactiveShape = (props: any) => { const { cx, cy, innerRadius, outerRadius, startAngle, endAngle, className } = props  return ( <Sector cx={cx} cy={cy} innerRadius={innerRadius} outerRadius={outerRadius} startAngle={startAngle} endAngle={endAngle} className={className} fill="" opacity={0.3} style={{ outline: "none" }} /> )} type DonutChartVariant = "donut" | "pie" type BaseEventProps = { eventType: "sector" categoryClicked: string [key: string]: number | string} type DonutChartEventProps = BaseEventProps | null | undefined interface DonutChartProps extends React.HTMLAttributes<HTMLDivElement> { data: Record<string, any>[] category: string value: string colors?: AvailableChartColorsKeys[] variant?: DonutChartVariant valueFormatter?: (value: number) => string label?: string showLabel?: boolean showTooltip?: boolean onValueChange?: (value: DonutChartEventProps) => void tooltipCallback?: (tooltipCallbackContent: TooltipProps) => void customTooltip?: React.ComponentType<TooltipProps>} const DonutChart = React.forwardRef<HTMLDivElement, DonutChartProps>( ( { data = [], value, category, colors = AvailableChartColors, variant = "donut", valueFormatter = (value: number) => value.toString(), label, showLabel = false, showTooltip = true, onValueChange, tooltipCallback, customTooltip, className, ...other }, forwardedRef, ) => { const CustomTooltip = customTooltip const [activeIndex, setActiveIndex] = React.useState<number | undefined>( undefined, ) const isDonut = variant === "donut" const parsedLabelInput = parseLabelInput(label, valueFormatter, data, value)  const categories = Array.from(new Set(data.map((item) => item[category]))) const categoryColors = constructCategoryColors(categories, colors)  const prevActiveRef = React.useRef<boolean | undefined>(undefined) const prevCategoryRef = React.useRef<string | undefined>(undefined)  const handleShapeClick = ( data: any, index: number, event: React.MouseEvent, ) => { event.stopPropagation() if (!onValueChange) return  if (activeIndex === index) { setActiveIndex(undefined) onValueChange(null) } else { setActiveIndex(index) onValueChange({ eventType: "sector", categoryClicked: data.payload[category], ...data.payload, }) } }  return ( <div ref={forwardedRef} className={cx("h-40 w-40", className)} {...other}> <ResponsiveContainer className="size-full"> <ReChartsDonutChart onClick={ onValueChange && activeIndex !== undefined ? () => { setActiveIndex(undefined) onValueChange(null) } : undefined } margin={{ top: 0, left: 0, right: 0, bottom: 0 }} > {showLabel && isDonut && ( <text className="fill-gray-700 dark:fill-gray-300" x="50%" y="50%" textAnchor="middle" dominantBaseline="middle" > {parsedLabelInput} </text> )} <Pie className={cx( "stroke-white dark:stroke-gray-950 [&_.recharts-pie-sector]:outline-hidden", onValueChange ? "cursor-pointer" : "cursor-default", )} data={parseData(data, categoryColors, category)} cx="50%" cy="50%" startAngle={90} endAngle={-270} innerRadius={isDonut ? "75%" : "0%"} outerRadius="100%" stroke="" strokeLinejoin="round" dataKey={value} nameKey={category} isAnimationActive={false} onClick={handleShapeClick} activeIndex={activeIndex} inactiveShape={renderInactiveShape} style={{ outline: "none" }} /> {showTooltip && ( <Tooltip wrapperStyle={{ outline: "none" }} isAnimationActive={false} content={({ active, payload }) => { const cleanPayload = payload ? payload.map((item: any) => ({ category: item.payload[category], value: item.value, color: categoryColors.get( item.payload[category], ) as AvailableChartColorsKeys, })) : []  const payloadCategory: string = cleanPayload[0]?.category  if ( tooltipCallback && (active !== prevActiveRef.current || payloadCategory !== prevCategoryRef.current) ) { tooltipCallback({ active, payload: cleanPayload, }) prevActiveRef.current = active prevCategoryRef.current = payloadCategory }  return showTooltip && active ? ( CustomTooltip ? ( <CustomTooltip active={active} payload={cleanPayload} /> ) : ( <ChartTooltip active={active} payload={cleanPayload} valueFormatter={valueFormatter} /> ) ) : null }} /> )} </ReChartsDonutChart> </ResponsiveContainer> </div> ) },) DonutChart.displayName = "DonutChart" export { DonutChart, type DonutChartEventProps, type TooltipProps }

Tremor copy-and-paste

Full customization with our copy-and-paste React components.

  • 35+

    Unique Components

  • 300+

    Block examples

import { DonutChart } from '@tremor/react'; export function DonutChartUsageExample() { return ( <DonutChart data={sales} category="sales" index="name" valueFormatter={valueFormatter} colors={['blue', 'cyan', 'indigo', 'violet', 'fuchsia']} className="w-40" /> );}

NPM Package

Get started quickly with a basic set of importable components.

  • 15K+

    Stargazers on GitHub

  • 300K+

    Monthly downloads