npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@reopt-ai/opt-charts

v1.8.2

Published

Data visualization components for Reopt interfaces. Recharts adapters, SVG visualizations, chart frames, and chart-specific shells.

Readme

@reopt-ai/opt-charts

Chart primitives, chart-specific shells, and metadata for the reopt design system. @reopt-ai/opt-ui/visuals keeps legacy compatibility re-exports, but new chart work should import this package directly.

Skill support

There is no dedicated opt-charts-install skill in reopt-ai/reopt-skills yet. If the app does not already use opt-ui, the opt-ui-install skill can prepare the shared Tailwind and theme foundation:

npx skills add reopt-ai/reopt-skills/opt-ui-install

It does not install opt-charts. Install this package manually with the command below.

Install

bun add @reopt-ai/opt-charts recharts

react and react-dom are peer dependencies. recharts is a peer so product apps can control the visualization runtime version.

Exports

| Export | Purpose | | ------------------------------ | --------------------------------------------------------- | | @reopt-ai/opt-charts | All client chart components, shells, utilities, and types | | @reopt-ai/opt-charts/time | Server-safe date math and time-axis functions | | @reopt-ai/opt-charts/visuals | Low-level chart visuals | | @reopt-ai/opt-charts/shells | Product-ready chart shells | | @reopt-ai/opt-charts/meta | Component metadata for docs and tooling |

Example

import { LineChart } from "@reopt-ai/opt-charts";

export function RevenueTrend() {
  return (
    <LineChart
      data={[
        { month: "Jan", revenue: 120 },
        { month: "Feb", revenue: 164 },
      ]}
      xKey="month"
      yKey="revenue"
    />
  );
}

Time-series example

TimeSeriesChart treats the x-axis as time rather than a list of labels. Pass explicit bounds and granularity so empty buckets, partial trailing buckets, annotations, and drag selection all share the same window.

import { TimeSeriesChart } from "@reopt-ai/opt-charts";

const from = Date.UTC(2026, 7, 24, 0);
const to = Date.UTC(2026, 7, 24, 4);

<TimeSeriesChart
  data={[
    { t: from, requests: 42 },
    { t: from + 3_600_000, requests: 88 },
    { t: from + 7_200_000, requests: 130 },
    { t: from + 10_800_000, requests: 104 },
  ]}
  series={[{ dataKey: "requests", name: "Requests", type: "area" }]}
  from={from}
  to={to}
  granularity="hour"
  timeZone="UTC"
  locale="en-US"
  gradient
  selectable
  onRangeSelect={(range) => console.log(range)}
  aria-label="Hourly requests"
/>;

Loading and small multiples

Use LoadingChart to preserve the chart's visual weight while a query is running. Use SmallMultiples when one shared scale and a repeated mark make comparisons across segments easier than overlapping every series in one plot.

import { LoadingChart, SmallMultiples } from "@reopt-ai/opt-charts";

return loading ? (
  <LoadingChart variant="area" height={220} />
) : (
  <SmallMultiples
    series={countrySeries}
    dataKey="events"
    from={from}
    to={to}
    granularity="day"
    sharedYAxis
    maxPanels={6}
    aria-label="Events by country"
  />
);

Drill into a retention cohort

Provide onCellSelect when a populated cell should open the cohort behind the percentage. Empty cells remain inert, and cellLabel gives every control a complete accessible name.

import { RetentionHeatmap, type RetentionCellRef } from "@reopt-ai/opt-charts";

const [selected, setSelected] = useState<RetentionCellRef | null>(null);

<RetentionHeatmap
  data={cohorts}
  periods={["Day 0", "Day 1", "Day 7", "Day 30"]}
  onCellSelect={setSelected}
  cellLabel={(cell) =>
    `${cell.cohort}, day ${cell.period}, ${cell.value}% retained`
  }
  aria-label="Weekly retention cohorts"
/>;

Draw a journey as a path flow

PathFlowChart takes the columns of a step-positioned journey — the top items at each step, plus how many sessions fell outside the top rows ("other"), ended there ("drop-off") or started there ("entry") — and the edges between adjacent columns. The grid variant draws fixed cards with a share bar; sankey draws nodes proportional to their count with a drop-off tail. Hovering a card lights up everything connected to it, and renderCardActions puts a menu inside a card without the menu's clicks reading as card clicks.

import { PathFlowChart, type PathFlowData } from "@reopt-ai/opt-charts";

const flow: PathFlowData = {
  steps: [
    { index: 0, rows: [{ key: "/", count: 1200 }], dropOffCount: 300 },
    {
      index: 1,
      rows: [
        { key: "/pricing", count: 560 },
        { key: "/docs", count: 380 },
      ],
      otherCount: 260,
    },
  ],
  edges: [
    {
      stepIndex: 0,
      source: "/",
      target: "/pricing",
      count: 480,
      avgSeconds: 42,
    },
    { stepIndex: 0, source: "/", target: "/docs", count: 300 },
    { stepIndex: 0, source: "/", target: null, count: 120 },
  ],
};

<PathFlowChart
  data={flow}
  variant="grid"
  onCardClick={(card) => console.log(card.key, card.stepIndex)}
  aria-label="User flow"
/>;

The model behind the layout is exported too: buildPathFlowModel, connectedPathFlowKeys and dominantPathFlowChain let a page answer "which chain is this card on" without re-deriving the geometry. FunnelChart gained a variant (list, bars, horizontal) so the chain can be previewed as a funnel in the same visual language.

Resolve time without React

Use @reopt-ai/opt-charts/time in Route Handlers, server code, URL parsers, and tests. The subpath has no React or Recharts dependency and uses the same date-math grammar and bucket calculations as TimeSeriesChart.

import {
  formatBucketLabel,
  resolveTimeRange,
  timeTicks,
} from "@reopt-ai/opt-charts/time";

const range = resolveTimeRange(
  { from: "now-7d/d", to: "now/d" },
  { now: Date.now(), timeZone: "Asia/Seoul" },
);

if (range) {
  const ticks = timeTicks(range.from, range.to, {
    granularity: "day",
    timeZone: "Asia/Seoul",
  });
  const labels = ticks.map((tick) =>
    formatBucketLabel(tick, "day", {
      locale: "ko-KR",
      timeZone: "Asia/Seoul",
    }),
  );
}

Development

bun run --filter @reopt-ai/opt-charts lint
bun run --filter @reopt-ai/opt-charts typecheck
bun run --filter @reopt-ai/opt-charts test
bun run --filter @reopt-ai/opt-charts build

The package emits ESM, CJS, and type declarations under dist/.