Skip to content
Native Animation
Habit Heatmap Grid animation preview

This preview uses fictional demo content. See the props below for the customization options available in this component.

npx nativeanimation add habit-heatmap-grid
Skia

Habit Heatmap Grid

A habit heatmap with a light wave and rolling streak count, with configurable history, streak, colors, and fonts.

$9 for this animation

Add to your project

Copy the component and its assets into your app with the Native Animation CLI, or paste the agent prompt into Claude Code, Codex or Cursor once the MCP server is connected.

About this animation

A habit heatmap with a light wave and rolling streak count, with configurable history, streak, colors, and fonts.

Why this one

Completing a day sends a brief pulse through the whole history instead of merely repainting a cell. The breathing outline makes today easy to find; a magnetic glass tooltip follows day inspection. The Aurora demo presents a complete meditation habit screen.

How it works

  1. Measure the container with onLayout; fit 18 columns of seven square cells into its width.
  2. Draw every cell in one Skia Canvas. A UI thread entrance clock reveals diagonal groups with 6 ms spacing.
  3. Share one completion progress between today's color, the drawn confirmation ring, and rolling number columns. Separate values handle pressing and the transient wave.
  4. Offset each cell's 290 ms light pulse by its Euclidean distance from today, at 8 ms per cell. The full celebration settles within 600 ms.
  5. Compose native tap and pan gestures for inspection, and tap and 600 ms long press for completion. Spring the tooltip toward the inspected cell.
  6. Reset with a 220 ms fade. Reduced motion removes entrance staggering, wave, breathing, and spatial travel.

Install

npx expo install @shopify/react-native-skia expo-blur expo-linear-gradient react-native-gesture-handler react-native-reanimated react-native-worklets

Use a GestureHandlerRootView in the host app. In web projects, load CanvasKit before mounting the component, using Skia's web loader. The catalog host must register this demo with its withSkia loader. Fonts are supplied by the host; the reusable component has no theme imports.

Usage

import HabitHeatmapGrid from './habit-heatmap-grid';

// Provide 126 consecutive days, in chronological order. The last day is today.
<HabitHeatmapGrid days={history} />;
const habit = useRef<HabitHeatmapGridRef>(null);

<HabitHeatmapGrid
  ref={habit}
  days={history}
  title="Evening practice"
  streak={12}
  targetMinutes={15}
  monthPercent={80}
  bestStreak={30}
  totalHours={24}
  waveDelay={10}
  onCompleteChange={saveCompletion}
/>;
// habit.current?.complete(), reset(), enter()

Props

PropTypeDefaultPurpose
daysreadonly HabitDay[]RequiredChronological history. The latest 126 days are shown; the last is today.
titlestring'Daily practice'Habit title.
streaknumber0Baseline streak before completing today.
targetMinutesnumber10Today's target in minutes.
monthPercentnumber0Percentage achieved this month.
bestStreaknumber0Longest streak in days.
totalHoursnumber0Total practice hours.
colorsreadonly [ string, string, string, string, string, ]LEVELSFive activity colors, from inactive to strongest.
waveDelaynumber8Wave travel time per cell of distance in milliseconds.
fontFamilystringundefinedUI font family.
titleFontFamilystringundefinedEditorial title font family.
monoFontFamilystringundefinedTabular number font family.
onCompleteChange( completed: boolean, ) => voidundefinedCalled when today's completion changes.
onDayInspect(day: HabitDay) => voidundefinedCalled when a day is inspected.
accessibilityLabelstring'Habit history'Grid label for assistive technology.
styleStyleProp<ViewStyle>undefinedContainer style.
testIDstringundefinedTest identifier.
durationnumber1700Heatmap breathing half-cycle in milliseconds. Spring physics and reduced-motion timing are unchanged.

The component owns today's temporary completion state; call reset() to undo. enter() replays the entrance independently. Month and weekday labels derive from your date range. Supply whole, nonnegative numbers for statistics.

Compatibility

Targets Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, worklets 0.13.0, and Skia 2.14.0. Uses the existing Expo Go compatible stack. Supports iOS, Android, and web. The demo is designed for 320×694 and 390×844 portrait containers.

Accessibility

The completion button exposes a button role, selection state, and activation action. Tap is an alternative to holding. The history exposes adjustable actions to inspect adjacent days without dragging. Status updates use a polite live region; touching outside the grid dismisses details. Reduced motion displays all cells immediately and completes without a traveling wave or spatial number animation.

Icons: Lucide (ISC), copied from the repository kit. Aurora's sparkles tile and palette also come from the kit.