# WHOOP strain gauge chart in React Native with Skia

> By Lawrence Arya, Founder & CEO of VP0. Published 2026-06-10. 10 min read.
> Source: https://vp0.com/blogs/whoop-strain-gauge-chart-react-native-skia

A strain gauge is a number made glanceable. Skia gives the gradient and glow; Reanimated sweeps the arc up to the value on the UI thread.

**TL;DR.** A WHOOP-style strain gauge in React Native is a circular arc that fills toward a value on a fixed scale, drawn with react-native-skia and animated with Reanimated. Skia is the right tool because the gauge needs a smooth gradient stroke, rounded caps, and a soft glow that SVG renders less cleanly and a chart library cannot give you. You draw a background track arc and a foreground arc whose sweep is driven by the value, then animate that sweep on the UI thread. Starting from a free VP0 design and letting Claude Code or Cursor read its source page gets the proportions right so you build the Skia canvas on top.

A WHOOP-style strain gauge in React Native is a circular arc that fills toward a value on a fixed scale, drawn with [react-native-skia](https://shopify.github.io/react-native-skia/) and animated with Reanimated. Skia is the right tool here because the gauge needs a smooth gradient stroke, rounded caps, and a soft glow that SVG renders less cleanly and a chart library cannot give you at all. You draw a background track arc and a foreground arc whose sweep is driven by the value, then animate that sweep on the UI thread so it counts up smoothly. The quickest way to get the proportions and colors right is to start from a free VP0 design and let Claude Code or Cursor read its source page, then build the Skia canvas on top.

A strain gauge is a single number made glanceable, so the bar is high: the arc has to read instantly, the gradient has to look intentional, and the fill animation has to be smooth on every device. The sections below cover the arc, the gradient, the animation, and the data behind it.

## How do you build a WHOOP-style strain gauge in React Native?

You draw two arcs in a Skia `Canvas`: a muted background track and a brighter foreground arc that sweeps from the start to the point representing the current value. Both are arcs of the same circle, usually leaving a gap at the bottom so the gauge reads as a dial rather than a full ring, and the foreground arc uses a gradient stroke with rounded ends.

react-native-skia, with more than 8,000 stars, renders this with a real graphics engine, so gradients, blurs, and stroke caps look the way they do in a design tool rather than approximated. The value maps to a sweep angle: a strain of zero is an empty foreground arc, the maximum fills the whole track, and anything between is a proportional sweep. Because Skia draws to a canvas, you describe the arc as a [path](https://shopify.github.io/react-native-skia/docs/shapes/path/) and stroke it, rather than composing views. For other Skia chart shapes, [the react-native-skia UI examples](/blogs/react-native-skia-ui-examples/) show the canvas patterns.

## Drawing the arc and the gradient

The gauge is a stroked arc path with a sweep gradient, rounded caps, and a gap at the bottom. You define the track once and the foreground arc with an animated end, both sharing the same geometry.

```jsx
import { Canvas, Path, SweepGradient, vec } from "@shopify/react-native-skia";

<Canvas style={{ width: 220, height: 220 }}>
  <Path path={trackPath} style="stroke" strokeWidth={18} strokeCap="round" color="#222" />
  <Path path={valuePath} style="stroke" strokeWidth={18} strokeCap="round">
    <SweepGradient c={vec(110, 110)} colors={["#3CE", "#5F8", "#FE5"]} />
  </Path>
</Canvas>;
```

The gradient is what gives the gauge its WHOOP-like quality: a sweep gradient that shifts color along the arc, from a cool low end to a warmer high end, so the color itself communicates intensity. Rounded stroke caps soften the ends, and a subtle shadow or blur behind the foreground arc adds the glow that makes it feel alive. Leaving a gap at the bottom, drawing the arc from about 135 degrees around to 45 degrees on the other side, reads as a gauge rather than a closed loop, which is the conventional shape for this kind of metric.

## Animating the fill

The fill animates by driving the foreground arc's end point from a Reanimated shared value, so the arc sweeps up to the value rather than appearing fully drawn. Skia integrates with [Reanimated](https://docs.swmansion.com/react-native-reanimated/), so you animate a progress value from zero to the target and recompute the arc path from it, and the drawing stays on the UI thread the same way a gesture animation would.

```jsx
const progress = useSharedValue(0);
useEffect(() => {
  progress.value = withTiming(value / MAX_STRAIN, { duration: 1200, easing: Easing.out(Easing.cubic) });
}, [value]);
```

The ease-out curve makes the gauge count up quickly and settle gently, which feels more satisfying than a linear fill. Pair the arc animation with the numeric value counting up in sync, so the number and the arc reach the target together. Keep the animation on the UI thread through Reanimated so it stays smooth even while the rest of the screen loads data. A 1.2-second fill is long enough to feel deliberate without making the user wait, and matching the number's count to the arc's sweep is the detail that makes it feel like one connected motion.

## The data behind the gauge

A strain gauge is only as good as the value it shows, so the data model matters as much as the drawing. The value sits on a fixed scale, WHOOP's strain runs zero to twenty-one, and the gauge's full sweep represents that maximum, so the same arc geometry works for any value by scaling the sweep. Decide the scale once and keep the gauge proportional to it.

Show the context the number needs: a label for what the metric is, the scale's maximum so the value has meaning, and optionally zones along the track for low, moderate, and high ranges. Avoid implying medical precision the data does not have; a strain or readiness score is an estimate, so the copy should present it as guidance rather than a clinical reading. The charting fundamentals for health metrics carry over from [a blood pressure log chart](/blogs/blood-pressure-log-chart-ui-react-native/) and [a custom screen-time chart](/blogs/screen-time-chart-custom-ui-react-native/), where honest framing of the numbers is part of the design.

## Making it smooth with AI and a real design

AI builders reach for the wrong tool and the wrong math on a gauge like this. Claude Code and Cursor will often try to build it from nested views and borders, which cannot produce the gradient and glow, or they compute the arc path incorrectly so the sweep does not match the value. The result looks roughly like a gauge and is subtly wrong in the geometry.

A real design plus a Skia instruction fixes it. When the gauge's size, gap, gradient, and stroke are already decided, the model draws a correct Skia arc instead of faking it with views, and you tell it to use react-native-skia with a sweep gradient rather than CSS-style borders. Starting from a free VP0 design gives that structure, since each design has a machine-readable source page Claude Code, Cursor, or Rork read from a pasted link. If you have an existing SVG gauge, [converting raw SVG to react-native-skia](/blogs/convert-raw-svg-to-react-native-skia-ai/) covers moving it onto the canvas.

## Common strain gauge mistakes

A few mistakes recur. Building the gauge from views and borders instead of Skia is the first, and it cannot produce the gradient stroke or glow that defines the look. Getting the arc math wrong so the sweep does not match the value is the second, which makes the gauge subtly lie about the number.

Drawing a full ring instead of a gauge with a bottom gap is the third, which loses the dial shape the metric conventionally uses. Animating the arc but not the number, or letting them finish at different times, is the fourth, which breaks the sense of one connected motion. The fifth is implying clinical precision in the copy, presenting an estimated score as if it were a measured medical reading; keep the framing as guidance. Using Skia, matching the sweep to the value exactly, and syncing the number to the arc are what make the gauge both correct and convincing.

## When a simpler indicator works

A full Skia gauge is not always warranted. For a value that does not need a gradient or a glow, a simple progress ring or a labeled bar communicates the same proportion with far less code, and it renders fine without a graphics engine. The Skia gauge earns its place when the metric is a hero element the user checks often and the visual quality is part of the product's identity.

If the gauge is a minor stat buried in a list, the heavier Skia approach is overkill, and a basic ring serves better. Matching the rendering effort to how prominent and how branded the gauge needs to be keeps you from pulling in a graphics engine for a number nobody studies. Decide by whether the gauge is a centerpiece or a detail, and remember that one well-made hero gauge often does more for an app's perceived quality than a screen full of average charts.

## Key takeaways: a strain gauge that reads instantly

Draw the gauge as two Skia arcs, a muted track and a gradient foreground with rounded caps and a bottom gap, and animate the foreground sweep from a Reanimated value with an ease-out curve. Use a sweep gradient so color communicates intensity, sync the numeric value to the arc so they finish together, and keep the scale fixed so any value maps to a proportional sweep. Frame the number as guidance rather than a clinical reading. Let an AI builder draw it from a real design, and steer it to Skia rather than bordered views. A commissioned data-visualization feature can cost $5,000 or more, while starting from a free VP0 design gives you the gauge layout for nothing, so your effort goes into the Skia drawing rather than the design.

You can [browse VP0 designs](/explore) to start your gauge from a real layout rather than a blank canvas, so the model draws a correct Skia arc against proportions that already look right.

## Frequently asked questions

### How do you build a WHOOP-style strain gauge in React Native?

Draw two arcs in a react-native-skia canvas: a muted background track and a gradient foreground arc with rounded caps, both leaving a gap at the bottom so it reads as a gauge. Map the value to a sweep angle on a fixed scale, and animate the foreground arc's sweep from a Reanimated shared value with an ease-out curve while the number counts up in sync. Starting from a free VP0 design gets the proportions and gradient right so you focus on the Skia drawing.

### Why use Skia instead of SVG for a gauge?

Skia is a real graphics engine, so it renders the sweep gradient, rounded caps, and soft glow that define the WHOOP look cleanly, where SVG approximates them and a chart library cannot produce them at all. It also animates smoothly on the UI thread through Reanimated. For a simple ring with a flat color, SVG or even a plain view is fine, but for the gradient-and-glow gauge specifically, Skia is the tool that gets the visual quality right.

### How do I animate the gauge fill smoothly?

Drive a progress value from zero to the target with a Reanimated shared value and an ease-out timing curve, then recompute the foreground arc's sweep from that progress so the arc fills up rather than appearing fully drawn. Keep it on the UI thread so it stays smooth while data loads, and animate the numeric value in sync so the number and the arc reach the target together. A roughly 1.2-second fill feels deliberate without making the user wait.

### Can VP0 provide a free React Native template for a gauge or chart?

Yes. VP0 is a free iOS app design library where every design has a machine-readable source page an AI builder reads from a pasted link, with React Native and SwiftUI variants. You start from the gauge design, with its size, gap, and gradient already decided, hand its source to Claude Code, Cursor, or Rork, and build the Skia canvas and animation on top, rather than guessing the arc geometry from a blank screen.

### What common errors happen when building a strain gauge?

The frequent ones are building it from views and borders instead of Skia so the gradient and glow are impossible, getting the arc math wrong so the sweep does not match the value, drawing a full ring instead of a gauge with a bottom gap, letting the number and the arc finish at different times, and implying clinical precision in copy for an estimated score. The fixes are Skia with a sweep gradient, exact sweep-to-value math, the conventional gauge gap, a synced number, and guidance-level framing.

## Frequently asked questions

### How do you build a WHOOP-style strain gauge in React Native?

Draw two arcs in a react-native-skia canvas: a muted background track and a gradient foreground arc with rounded caps, both leaving a gap at the bottom so it reads as a gauge. Map the value to a sweep angle on a fixed scale, and animate the foreground arc's sweep from a Reanimated shared value with an ease-out curve while the number counts up in sync. Starting from a free VP0 design gets the proportions and gradient right so you focus on the Skia drawing.

### Why use Skia instead of SVG for a gauge?

Skia is a real graphics engine, so it renders the sweep gradient, rounded caps, and soft glow that define the WHOOP look cleanly, where SVG approximates them and a chart library cannot produce them at all. It also animates smoothly on the UI thread through Reanimated. For a simple ring with a flat color, SVG or even a plain view is fine, but for the gradient-and-glow gauge specifically, Skia is the tool that gets the visual quality right.

### How do I animate the gauge fill smoothly?

Drive a progress value from zero to the target with a Reanimated shared value and an ease-out timing curve, then recompute the foreground arc's sweep from that progress so the arc fills up rather than appearing fully drawn. Keep it on the UI thread so it stays smooth while data loads, and animate the numeric value in sync so the number and the arc reach the target together. A roughly 1.2-second fill feels deliberate without making the user wait.

### Can VP0 provide a free React Native template for a gauge or chart?

Yes. VP0 is a free iOS app design library where every design has a machine-readable source page an AI builder reads from a pasted link, with React Native and SwiftUI variants. You start from the gauge design, with its size, gap, and gradient already decided, hand its source to Claude Code, Cursor, or Rork, and build the Skia canvas and animation on top, rather than guessing the arc geometry from a blank screen.

### What common errors happen when building a strain gauge?

The frequent ones are building it from views and borders instead of Skia so the gradient and glow are impossible, getting the arc math wrong so the sweep does not match the value, drawing a full ring instead of a gauge with a bottom gap, letting the number and the arc finish at different times, and implying clinical precision in copy for an estimated score. The fixes are Skia with a sweep gradient, exact sweep-to-value math, the conventional gauge gap, a synced number, and guidance-level framing.

---
*Published on the [VP0 Journal](https://vp0.com/blogs). Free to read, index and cite with attribution.*
