1.0.0-beta.3

Adds AreaChart, ChartCrosshair, ChartIndicator, EmptyState, and Segment components, unifies press-driven overlays across cartesian charts, removes LineChart.Tooltip and LineChart.Crosshair, and cleans up ProgressButton press-out handling.

May 2026

The third beta of HeroUI Native Pro lands five new components — AreaChart, ChartCrosshair, ChartIndicator, EmptyState, and Segment — and unifies press-driven UI across every cartesian chart. LineChart and BarChart now share the same standalone crosshair and indicator primitives.

Try It on Your Device

Don't have the HeroUI Native app yet? Download it below.
Download on App StoreAndroid · Coming soon

Installation

Follow the installation guide to authenticate the private registry with your HEROUI_PERSONAL_TOKEN, install heroui-native-pro, and wire up HeroUINativeProvider.

What's New

New Components

This release introduces 5 new components spanning charts, data display, and navigation:

  • AreaChart: victory-native-powered area chart with Area, AreaRange, and StackedArea compound parts. (Documentation)
  • ChartCrosshair: Skia vertical rule with an RN tooltip overlay (Anchor / Value / ValueLabel) shared across every cartesian chart. (Documentation)
  • ChartIndicator: Themed Skia double-dot marker (outer halo + inner dot) for press-driven point markers. (Documentation)
  • EmptyState: Compound primitive for empty / zero-state messaging with Header, Media, Title, Description, and Content parts. (Documentation)
  • Segment: Segmented control built on Tabs with Group, ScrollView, Indicator, Item, Label, and Separator compounds. (Documentation)

AreaChart

A Skia-accelerated area chart for visualizing trends, stacked contributions, and confidence bands. Built on victory-native and wrapped in a themed outer View, with the same compound, theming, and animation cascade conventions as LineChart and BarChart. Pair it with ChartCrosshair and ChartIndicator to add press-driven overlays.

Features:

  • Compound parts — AreaChart.Area, AreaChart.AreaRange, AreaChart.StackedArea
  • Themed fills via Uniwind colorClassName (e.g. accent-chart-1, accent-chart-3) with full accent-chart-* token support
  • Built-in draw-on animation, gated by the cascading animation="disable-all" prop through AnimationSettingsProvider
  • Configurable curveType (natural, linear, monotoneX, etc.) and per-area animate config for path-interpolated data transitions
  • Skia LinearGradient composes as a child of AreaChart.Area for gradient fills
  • useAreaPath and useStackedAreaPaths hooks re-exported for custom rendering
  • victory-native stays an optional dependency — only loaded when a chart component is imported

Usage:

import { AreaChart } from "heroui-native-pro";

const DATA = [
  { month: "Jan", revenue: 120 },
  { month: "Feb", revenue: 180 },
  { month: "Mar", revenue: 150 },
];

export function Example() {
  return (
    <AreaChart
      data={DATA}
      xKey="month"
      yKeys={["revenue"]}
      wrapperClassName="h-48"
    >
      {({ points, chartBounds }) => (
        <AreaChart.Area
          points={points.revenue}
          y0={chartBounds.bottom}
        />
      )}
    </AreaChart>
  );
}

For complete documentation and examples, see the AreaChart component page.

ChartCrosshair

A vertical rule and tooltip overlay that highlight the pressed point on a chart. The Skia rule renders inside the chart canvas, while a sibling React Native overlay (ChartCrosshair.Value) hosts the tooltip pill — measured, centered, and clamped against chartBounds, with a label that updates on the UI thread via an internal ReText (read-only Reanimated TextInput) bridge.

Features:

  • Compound parts — ChartCrosshair, ChartCrosshair.Anchor, ChartCrosshair.Value, ChartCrosshair.ValueLabel
  • variantdashed (themed DashPathEffect) or solid unbroken stroke
  • Custom color, strokeWidth, and overrideable DashPathEffect via children
  • Driven by useChartPressState shared values from victory-native — no React renders on press
  • React Native value pill rendered outside the Skia canvas: auto-measures its own width, centers on x, and clamps to chartBounds via onChartBoundsChange
  • ChartCrosshair.ValueLabel reads a useDerivedValue shared string from context, so label text updates entirely on the UI thread
  • Works uniformly across LineChart, BarChart, and AreaChart

Usage:

import { LineChart, ChartCrosshair, ChartIndicator } from "heroui-native-pro";
import { useChartPressState } from "victory-native";
import { useDerivedValue } from "react-native-reanimated";

export function Example() {
  const { state, isActive } = useChartPressState({
    x: "" as string,
    y: { revenue: 0 },
  });

  const value = useDerivedValue(
    () => `$${state.y.revenue.value.value.toFixed(0)}`
  );

  return (
    <ChartCrosshair.Anchor>
      <LineChart
        data={DATA}
        xKey="month"
        yKeys={["revenue"]}
        chartPressState={state}
        wrapperClassName="h-48"
      >
        {({ points, chartBounds }) => (
          <>
            <LineChart.Line points={points.revenue} />
            {isActive ? (
              <>
                <ChartCrosshair
                  x={state.x.position}
                  top={chartBounds.top}
                  bottom={chartBounds.bottom}
                />
                <ChartIndicator
                  x={state.x.position}
                  y={state.y.revenue.position}
                />
              </>
            ) : null}
          </>
        )}
      </LineChart>
      <ChartCrosshair.Value value={value}>
        <ChartCrosshair.ValueLabel />
      </ChartCrosshair.Value>
    </ChartCrosshair.Anchor>
  );
}

For complete documentation and examples, see the ChartCrosshair component page.

ChartIndicator

A themed Skia double-dot marker — outer halo plus inner dot — that follows the pressed point on a chart. Drop-in primitive that pairs with ChartCrosshair (or stands alone) for line, area, and bar press interactions.

Features:

  • Driven by useChartPressState shared values for x and y
  • innerRadius (default 5) and outerRadius (default 7) for sizing
  • innerColor and outerColor overrides on top of the themed --color-background halo and --color-chart-3 dot
  • Extra Skia Circle props are forwarded to the inner circle for stroke, opacity, and other effects
  • Renders inside the chart canvas alongside ChartCrosshair for a complete press-overlay system

Usage:

import { LineChart, ChartIndicator } from "heroui-native-pro";
import { useChartPressState } from "victory-native";

export function Example() {
  const { state, isActive } = useChartPressState({
    x: 0,
    y: { value: 0 },
  });

  return (
    <LineChart
      data={DATA}
      xKey="day"
      yKeys={["value"]}
      chartPressState={state}
      wrapperClassName="h-48"
    >
      {({ points }) => (
        <>
          <LineChart.Line points={points.value} />
          {isActive ? (
            <ChartIndicator
              x={state.x.position}
              y={state.y.value.position}
            />
          ) : null}
        </>
      )}
    </LineChart>
  );
}

For complete documentation and examples, see the ChartIndicator component page.

EmptyState

A placeholder for empty views with an optional icon, title, description, and call-to-action area. Use it for empty inboxes, no-search-results screens, first-run states, or any zero-data UI that previously had to be hand-rolled.

Features:

  • Compound parts — EmptyState.Header, EmptyState.Media, EmptyState.Title, EmptyState.Description, EmptyState.Content
  • EmptyState.Media ships default and icon variants (icon variant renders a circular muted surface)
  • EmptyState.Title rendered with accessibilityRole="header" for screen readers
  • All sub-components are optional — drop Content for header-only states, omit Media for text-only layouts
  • Root animation="disable-all" cascades through AnimationSettingsProvider to every animated descendant

Usage:

import { Button, EmptyState } from "heroui-native-pro";
import { BellIcon } from "lucide-react-native";

export function Example() {
  return (
    <EmptyState>
      <EmptyState.Header>
        <EmptyState.Media variant="icon">
          <BellIcon size={20} />
        </EmptyState.Media>
        <EmptyState.Title>No notifications yet</EmptyState.Title>
        <EmptyState.Description>
          New activity will show up here as it happens.
        </EmptyState.Description>
      </EmptyState.Header>
      <EmptyState.Content>
        <Button variant="outline">Refresh</Button>
      </EmptyState.Content>
    </EmptyState>
  );
}

For complete documentation and examples, see the EmptyState component page.

Segment

A segmented control for toggling between a small set of mutually exclusive options. Built on top of HeroUI Native's Tabs (variant="primary") so style overrides and animation hooks (useTabsMeasurements, useTabsTrigger) stay consistent with the underlying primitive.

Features:

  • Compound parts — Segment.Group, Segment.ScrollView, Segment.Indicator, Segment.Item, Segment.Label, Segment.Separator
  • Controlled (value / onValueChange) and uncontrolled (defaultValue) modes via useControllableState
  • sizesm, md, lg cascades padding, indicator radius, and label typography across every part
  • isDisabled cascades from the root through SegmentContext and merges per-item flags
  • Segment.ScrollView wraps Tabs.ScrollView for horizontally scrollable rows when items overflow
  • Segment.Indicator inherits Tabs animation props (animation, isAnimatedStyleActive) for full Reanimated control
  • Segment.Separator accepts betweenValues so dividers auto-hide when one of their neighbors is selected

Usage:

import { Segment } from "heroui-native-pro";

export function Example() {
  return (
    <Segment defaultValue="dashboard">
      <Segment.Group>
        <Segment.Indicator />
        <Segment.Item value="dashboard">
          <Segment.Label>Dashboard</Segment.Label>
        </Segment.Item>
        <Segment.Separator betweenValues={["dashboard", "analytics"]} />
        <Segment.Item value="analytics">
          <Segment.Label>Analytics</Segment.Label>
        </Segment.Item>
      </Segment.Group>
    </Segment>
  );
}

For complete documentation and examples, see the Segment component page.

Unified Chart Press Overlays

ChartCrosshair and ChartIndicator are now standalone primitives that work uniformly across every cartesian chart. Previously, LineChart shipped its own bundled LineChart.Tooltip and LineChart.Crosshair Skia compounds; there was no equivalent for BarChart or AreaChart, and no React Native overlay label that could sit on top of the Skia canvas.

Supported components:

The new pattern reads chartBounds from onChartBoundsChange and threads useChartPressState shared values through both Skia primitives and the RN value overlay — so press-driven UI is one mental model for every chart in the library.

Component Improvements

LineChart and BarChart Migration

The LineChart and BarChart example screens have been migrated to the new ChartCrosshair + ChartIndicator pattern.

Improvements:

  • Press-driven crosshair, indicator dot, and tooltip pill now share the same primitives across both charts
  • chartBounds sourced from onChartBoundsChange for consistent overlay clamping
  • React Native value labels render on top of the Skia canvas via the new ReText bridge — no more in-canvas-only tooltips
  • Component docs updated in lockstep with the new APIs

ProgressButton Press-Out Cleanup

The ProgressButton handlePressOut flow has been simplified. The redundant cancelAnimation(progress) call has been removed — resetProgress() already supersedes any in-flight animation by assigning a new withSpring value to progress, making the explicit cancel unnecessary.

Improvements:

  • handlePressOut now relies on resetProgress() alone to drive the value back to 0
  • Unused cancelAnimation import removed from progress-button.animation.ts
  • No visual or behavioral change for consumers — reset still runs as a spring (or instantly when animations are disabled)

⚠️ Breaking Changes

Removed LineChart.Tooltip and LineChart.Crosshair

LineChart.Tooltip and LineChart.Crosshair have been removed in favor of the new standalone ChartCrosshair and ChartIndicator exports from heroui-native-pro. The new primitives accept the same x / y / top / bottom props and the same useChartPressState wiring, so the migration is a like-for-like replacement.

Migration:

Replace LineChart.Tooltip with ChartIndicator, and LineChart.Crosshair with ChartCrosshair. For React Native value labels, wrap the chart in ChartCrosshair.Anchor and render ChartCrosshair.Value (with ChartCrosshair.ValueLabel) as a sibling.

// Before
import { LineChart } from "heroui-native-pro";
import { useChartPressState } from "victory-native";

const { state, isActive } = useChartPressState({
  x: 0,
  y: { value: 0 },
});

<LineChart
  data={DATA}
  xKey="day"
  yKeys={["value"]}
  chartPressState={state}
>
  {({ points, chartBounds }) => (
    <>
      <LineChart.Line points={points.value} />
      {isActive ? (
        <>
          <LineChart.Crosshair
            x={state.x.position}
            top={chartBounds.top}
            bottom={chartBounds.bottom}
          />
          <LineChart.Tooltip
            x={state.x.position}
            y={state.y.value.position}
          />
        </>
      ) : null}
    </>
  )}
</LineChart>

// After
import {
  LineChart,
  ChartCrosshair,
  ChartIndicator,
} from "heroui-native-pro";
import { useChartPressState } from "victory-native";
import { useDerivedValue } from "react-native-reanimated";

const { state, isActive } = useChartPressState({
  x: "" as string,
  y: { value: 0 },
});

const label = useDerivedValue(
  () => `${state.y.value.value.value.toFixed(0)}`
);

<ChartCrosshair.Anchor>
  <LineChart
    data={DATA}
    xKey="day"
    yKeys={["value"]}
    chartPressState={state}
  >
    {({ points, chartBounds }) => (
      <>
        <LineChart.Line points={points.value} />
        {isActive ? (
          <>
            <ChartCrosshair
              x={state.x.position}
              top={chartBounds.top}
              bottom={chartBounds.bottom}
            />
            <ChartIndicator
              x={state.x.position}
              y={state.y.value.position}
            />
          </>
        ) : null}
      </>
    )}
  </LineChart>
  <ChartCrosshair.Value value={label}>
    <ChartCrosshair.ValueLabel />
  </ChartCrosshair.Value>
</ChartCrosshair.Anchor>;

Available crosshair variants:

  • "dashed" — Themed DashPathEffect rule (default)
  • "solid" — Unbroken stroke

When using ChartCrosshair.Anchor, the wrapped chart's wrapperClassName must not contain padding (e.g. p-*, px-*, py-*) — the anchor measures positions in the chart's native coordinate space, so any padding offsets the chart relative to the anchor and breaks centering / clamping of ChartCrosshair.Value. Apply spacing on a parent container instead.

Updated Documentation

The following documentation pages have been updated to reflect the changes in this release:

  • AreaChart — New component page with basic, gradient, curve type, animated, stacked, and area-range examples
  • ChartCrosshair — New component page covering the Skia rule, RN value overlay, dashed / solid variants, and useDerivedValue label patterns
  • ChartIndicator — New component page covering basic usage, custom radii, custom colors, and forwarded Skia props
  • EmptyState — New component page with header, icon media, action, and animation-cascade examples
  • Segment — New component page covering controlled / uncontrolled selection, sizes, scrollable layouts, separators, and disabled cascading
  • LineChartLineChart.Tooltip and LineChart.Crosshair removed; press-overlay examples migrated to ChartCrosshair + ChartIndicator

On this page