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

expo-pedometer

v1.3.0

Published

HealthKit and Health Connect step count module for Expo

Downloads

878

Readme

expo-pedometer

Expo module for reading today's step count from platform health data.

  • iOS reads cumulative step count from HealthKit.
  • Android reads Health Connect StepsRecord aggregates. When Health Connect is not available on the device, it falls back to the Sensor.TYPE_STEP_COUNTER hardware sensor; the fallback count resets at midnight in the device's time zone.
  • Web is unavailable and reports denied permissions.

Install

npx expo install expo-pedometer

Add the config plugin:

{
  "expo": {
    "plugins": [
      [
        "expo-pedometer",
        {
          "iosHealthKitPermission": "Allow this app to read your Apple Health step count to show your daily walking progress.",
          "iosHealthKitBackgroundDelivery": true,
          "androidHealthConnectPrivacyPolicyUrl": "https://example.com/privacy",
          "androidHealthConnectBackgroundRead": true
        }
      ]
    ]
  }
}

The plugin adds:

  • iOS NSHealthShareUsageDescription and HealthKit entitlement.
  • iOS HealthKit background delivery entitlement when iosHealthKitBackgroundDelivery is enabled.
  • Android android.permission.health.READ_STEPS.
  • Android android.permission.ACTIVITY_RECOGNITION for the sensor fallback and background updates on Android 14+.
  • Android android.permission.FOREGROUND_SERVICE and android.permission.FOREGROUND_SERVICE_HEALTH when androidHealthConnectBackgroundRead is enabled.
  • Android android.permission.health.READ_HEALTH_DATA_IN_BACKGROUND when androidHealthConnectBackgroundRead is enabled.
  • Android Health Connect package query and permission rationale manifest entries.

AndroidX Health Connect requires Android minSdkVersion 26 or higher. Configure it in your app, for example with expo-build-properties:

[
  "expo-build-properties",
  {
    "android": {
      "minSdkVersion": 26
    }
  }
]

API

enum PermissionStatus {
  GRANTED = "granted",
  UNDETERMINED = "undetermined",
  DENIED = "denied",
}

type PermissionResponse = {
  status: PermissionStatus;
  canAskAgain: boolean;
};

export const isAvailableAsync: () => Promise<boolean>;
export const getPermissionsAsync: () => Promise<PermissionResponse>;
export const requestPermissionsAsync: () => Promise<PermissionResponse>;
export const isBackgroundAvailableAsync: () => Promise<boolean>;
export const getBackgroundPermissionsAsync: () => Promise<PermissionResponse>;
export const requestBackgroundPermissionsAsync: () => Promise<PermissionResponse>;
export const startStepCountUpdatesAsync: (taskName: string) => Promise<void>;
export const stopStepCountUpdatesAsync: (taskName: string) => Promise<void>;
export const getTodayStepCountAsync: () => Promise<number>;
export const usePermissions: () => [
  isAvailable: boolean | null,
  permission: PermissionResponse | null,
  requestPermission: () => Promise<PermissionResponse>,
  getPermission: () => Promise<PermissionResponse>,
];
export const useBackgroundPermissions: () => [
  isAvailable: boolean | null,
  permission: PermissionResponse | null,
  requestPermission: () => Promise<PermissionResponse>,
  getPermission: () => Promise<PermissionResponse>,
];

Example:

import { PermissionStatus, getTodayStepCountAsync, usePermissions } from "expo-pedometer";
import { Button } from "react-native";

export function StepCount() {
  const [isAvailable, permission, requestPermission] = usePermissions();

  async function requestAndReadSteps() {
    const nextPermission = await requestPermission();
    if (nextPermission.status === PermissionStatus.GRANTED) {
      const steps = await getTodayStepCountAsync();
      console.log(steps);
    }
  }

  if (isAvailable === false) {
    return null;
  }

  return (
    <Button
      title={permission?.status ?? "loading"}
      onPress={requestAndReadSteps}
    />
  );
}

usePermissions() fetches availability and the current permission when the component mounts. Calling requestPermission() or getPermission() updates the returned permission state.

Background Step Count Task

Install TaskManager before using background updates. Enable iosHealthKitBackgroundDelivery on iOS or androidHealthConnectBackgroundRead on Android in the config plugin.

npx expo install expo-task-manager

Define the task in global scope so Expo can load it while the app is backgrounded:

import {
  PermissionStatus,
  requestBackgroundPermissionsAsync,
  requestPermissionsAsync,
  startStepCountUpdatesAsync,
  type StepCountTaskData,
} from "expo-pedometer";
import * as TaskManager from "expo-task-manager";

const STEP_COUNT_TASK = "step-count-updates";

TaskManager.defineTask<StepCountTaskData>(STEP_COUNT_TASK, async ({ data, error }) => {
  if (error) {
    console.error("Step count task failed", error);
    return;
  }

  console.log("Updated step count", data.steps, new Date(data.observedAt));
  // Update app-owned notifications, widgets, or storage here.
});

export async function enableStepCountUpdates() {
  const permission = await requestPermissionsAsync();
  if (permission.status !== PermissionStatus.GRANTED) {
    return;
  }

  const backgroundPermission = await requestBackgroundPermissionsAsync();
  if (backgroundPermission.status === PermissionStatus.GRANTED) {
    await startStepCountUpdatesAsync(STEP_COUNT_TASK);
  }
}

The task receives today's cumulative steps and an observedAt Unix timestamp in milliseconds. Use stopStepCountUpdatesAsync() to stop updates.

HealthKit has no separate background permission. The iOS background permission APIs return granted when HealthKit is available and denied otherwise, while startStepCountUpdatesAsync() controls delivery.

Android Background Step Count Updates

Enable androidHealthConnectBackgroundRead in the config plugin before requesting background permission. Android background updates use a foreground service. Set androidForegroundServiceNotificationId to reuse an ongoing notification posted by the app.

[
  "expo-pedometer",
  {
    "androidForegroundServiceNotificationId": 1001
  }
]

Android Rationale Localization

androidHealthConnectRationaleTitle and androidHealthConnectRationaleDescription are optional overrides. They can be literal strings or Android string resource references.

If the overrides are omitted, the Android rationale screen reads these app string resources:

  • expo_pedometer_health_connect_rationale_title
  • expo_pedometer_health_connect_rationale_description

If those resources are not defined, built-in English defaults are used.

[
  "expo-pedometer",
  {
    "androidHealthConnectRationaleTitle": "Step count access",
    "androidHealthConnectRationaleDescription": "Step count is read from Health Connect to show today's walking progress.",
    "androidHealthConnectPrivacyPolicyUrl": "@string/privacy_policy_url"
  }
]

Apps using config plugins can provide localized values by generating those resource names in localized Android resource folders.

Platform Notes

getTodayStepCountAsync() should be called after isAvailableAsync() and a granted permission response.

iOS uses Apple Health's cumulative step count, including sources such as Apple Watch.

HealthKit controls background delivery timing and does not use a fixed polling interval. iOS does not relaunch the app after the user force-quits it.

HealthKit does not report whether a read data type was granted or denied. On iOS, granted means the authorization request completed. If step read access was denied, the count is 0.

Android Health Connect availability depends on device, OS version, and Health Connect installation state. When unavailable, the module uses the step counter sensor when present.

Health data permissions can require store privacy declarations and an app privacy policy before release.