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

react-native-workouts

v0.2.3

Published

React Native Expo module for Apple WorkoutKit - create, preview, and sync custom workouts to Apple Watch

Readme

react-native-workouts-banner

react-native-workouts

🏋️‍♀️ React Native Expo module for Apple WorkoutKit — create, preview, and sync workouts to Apple Watch.

Features

  • ✨ Create custom interval workouts (warmup, blocks, cooldown, alerts)
  • 🎯 Create single-goal workouts (distance / time / energy)
  • 🏃‍♂️ Create pacer workouts (pace / speed targets)
  • 🧩 Create multisport Swim / Bike / Run workouts
  • 👀 Preview with Apple’s system UI (includes “Add to Watch” / “Send to Watch”)
  • ⌚️ Schedule & sync to the Apple Watch Workout app
  • 🧠 Hooks-first API + stateful WorkoutPlan handle (Expo Shared Object)
  • ✅ Full TypeScript support

Requirements

  • iOS 17.0+ at runtime (WorkoutKit)
  • Tested with Expo SDK 54+
  • Apple Watch paired with iPhone (for workout sync)

Installation

npm install react-native-workouts
# or
yarn add react-native-workouts
# or
pnpm add react-native-workouts

Info.plist Configuration

Add the following keys to your Info.plist for HealthKit access:

<key>NSHealthShareUsageDescription</key>
<string>This app needs access to health data to sync workouts.</string>
<key>NSHealthUpdateUsageDescription</key>
<string>This app needs to write workout data.</string>

Usage

🔐 Authorization (hook)

Before previewing/scheduling, request authorization (HealthKit / WorkoutKit):

import { useWorkoutAuthorization } from "react-native-workouts";

export function MyScreen() {
  const { status, request, isLoading, error } = useWorkoutAuthorization();

  // status: 'authorized' | 'notDetermined' | 'denied' | 'unknown' | null
  // request(): prompts the system dialog (if needed) and returns the new status

  return null;
}

🚀 Quick start (hooks-first WorkoutPlan)

Hooks create a stateful WorkoutPlan shared object (Expo Shared Object). You call methods on the plan:

  • plan.preview() — opens Apple’s workout preview UI (includes “Add/Send to Watch”)
  • plan.scheduleAndSync(date) — schedules using Apple’s WorkoutScheduler (this is what syncs it to the Watch)
  • plan.export() — returns { id, kind, config } so you can persist/share and recreate later
import { useMemo } from "react";
import {
  type CustomWorkoutConfig,
  useCustomWorkout,
  useWorkoutAuthorization,
} from "react-native-workouts";

export function MyWorkoutScreen() {
  const { status, request } = useWorkoutAuthorization();

  const config = useMemo<CustomWorkoutConfig>(
    () => ({
      activityType: "running",
      locationType: "outdoor",
      displayName: "Morning Intervals",
      warmup: { goal: { type: "time", value: 5, unit: "minutes" } },
      blocks: [
        {
          iterations: 4,
          steps: [
            {
              purpose: "work",
              goal: { type: "distance", value: 400, unit: "meters" },
              alert: { type: "pace", min: 4, max: 5, unit: "min/km" },
            },
            {
              purpose: "recovery",
              goal: { type: "time", value: 90, unit: "seconds" },
            },
          ],
        },
      ],
      cooldown: { goal: { type: "time", value: 5, unit: "minutes" } },
    }),
    [],
  );

  const { plan, isLoading, error } = useCustomWorkout(config);

  const preview = async () => {
    if (!plan) return;
    if (status !== "authorized") await request();
    await plan.preview();
  };

  const scheduleTomorrowMorning = async () => {
    if (!plan) return;
    if (status !== "authorized") await request();
    await plan.scheduleAndSync({
      year: 2026,
      month: 1,
      day: 12,
      hour: 7,
      minute: 0,
    });
  };

  return null;
}

🎯 Single goal workouts (hook)

import { useMemo } from "react";
import type { SingleGoalWorkoutConfig } from "react-native-workouts";
import { useSingleGoalWorkout } from "react-native-workouts";

export function FiveKScreen() {
  const config = useMemo<SingleGoalWorkoutConfig>(
    () => ({
      activityType: "running",
      locationType: "outdoor",
      displayName: "5K Run",
      goal: { type: "distance", value: 5, unit: "kilometers" },
    }),
    [],
  );

  const { plan } = useSingleGoalWorkout(config);
  return null;
}

🏃‍♂️ Pacer workouts (hook)

import { useMemo } from "react";
import type { PacerWorkoutConfig } from "react-native-workouts";
import { usePacerWorkout } from "react-native-workouts";

export function TempoRunScreen() {
  const config = useMemo<PacerWorkoutConfig>(
    () => ({
      activityType: "running",
      locationType: "outdoor",
      displayName: "Tempo Run",
      target: { type: "pace", value: 5, unit: "min/km" },
    }),
    [],
  );

  const { plan } = usePacerWorkout(config);
  return null;
}

🧩 Swim / Bike / Run (multisport) (hook)

import { useMemo } from "react";
import type { SwimBikeRunWorkoutConfig } from "react-native-workouts";
import { useSwimBikeRunWorkout } from "react-native-workouts";

export function TriathlonScreen() {
  const config = useMemo<SwimBikeRunWorkoutConfig>(
    () => ({
      displayName: "Sprint Triathlon",
      activities: [
        { type: "swimming", locationType: "indoor" },
        { type: "cycling", locationType: "outdoor" },
        { type: "running", locationType: "outdoor" },
      ],
    }),
    [],
  );

  const { plan } = useSwimBikeRunWorkout(config);
  return null;
}

📦 Persist / share a plan (plan.export())

plan.export() does not export a .workout file.

It returns a small JSON payload you can store in your backend and later recreate:

  • id: UUID of the WorkoutPlan instance
  • kind: "custom" | "singleGoal" | "pacer" | "swimBikeRun"
  • config: the original config used to create the plan

📅 Managing scheduled workouts (hook)

import { useScheduledWorkouts } from "react-native-workouts";

export function ScheduledWorkoutsScreen() {
  const { workouts, remove, removeAll, schedule, isLoading, error } =
    useScheduledWorkouts();

  // schedule(plan, date) calls plan.scheduleAndSync(date) and reloads the list
  return null;
}

API Reference

Types

Activity Types

type ActivityType =
  | "running"
  | "cycling"
  | "walking"
  | "hiking"
  | "swimming"
  | "rowing"
  | "elliptical"
  | "stairClimbing"
  | "highIntensityIntervalTraining"
  | "yoga"
  | "functionalStrengthTraining"
  | "traditionalStrengthTraining"
  | "dance"
  | "jumpRope"
  | "coreTraining"
  | "pilates"
  | "kickboxing"
  | "stairs"
  | "stepTraining"
  | "wheelchairRunPace"
  | "wheelchairWalkPace";

Goal Types

type WorkoutGoal =
  | { type: "open" }
  | { type: "distance"; value: number; unit?: DistanceUnit }
  | { type: "time"; value: number; unit?: TimeUnit }
  | { type: "energy"; value: number; unit?: EnergyUnit };

Alert Types

type WorkoutAlert =
  | { type: "heartRate"; zone: number }
  | { type: "heartRate"; min: number; max: number }
  | { type: "pace"; min: number; max: number; unit?: PaceUnit }
  | { type: "speed"; min: number; max: number; unit?: SpeedUnit }
  | { type: "cadence"; min: number; max: number }
  | { type: "power"; min: number; max: number };

Units

type DistanceUnit =
  | "meters"
  | "m"
  | "kilometers"
  | "km"
  | "miles"
  | "mi"
  | "yards"
  | "yd"
  | "feet"
  | "ft";
type TimeUnit =
  | "seconds"
  | "s"
  | "sec"
  | "minutes"
  | "min"
  | "hours"
  | "h"
  | "hr";
type EnergyUnit = "kilocalories" | "kcal" | "cal" | "kilojoules" | "kj";
type SpeedUnit =
  | "metersPerSecond"
  | "mps"
  | "m/s"
  | "kilometersPerHour"
  | "kph"
  | "km/h"
  | "milesPerHour"
  | "mph";
type PaceUnit = "minutesPerKilometer" | "min/km" | "minutesPerMile" | "min/mi";

Notes

  • Swimming workouts do not support custom intervals, use SingleGoalWorkout instead
  • Scheduled workouts appear in the Workout app on Apple Watch and Fitness app on iPhone
  • Workouts display your app's icon and name in the Workout app
  • The preview APIs use Apple’s system UI (workoutPreview) and require iOS 17+

Deployment target note

This library is built on Apple's WorkoutKit (introduced in iOS 17). To avoid forcing apps to raise their deployment target just to install the package, the iOS native code:

  • weak-links WorkoutKit
  • gates all exported APIs behind #available(iOS 17.0, *)

On iOS < 17, calling any WorkoutKit API will reject/throw with an Unavailable error.

License

MIT