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-dynamic-island-telemetry

v0.1.0

Published

Unified React Native hook that drives iOS Live Activities / Dynamic Island and Android persistent notification progress cards from a single JavaScript payload.

Readme

react-native-dynamic-island-telemetry

Unified React Native hook that drives iOS Live Activities / Dynamic Island and Android persistent notification progress cards from a single JavaScript payload — with a safe in-memory JS fallback everywhere else.

const { startActivity, updateActivity, endActivity } = useLiveTelemetry();

await startActivity({ title: 'Thai Basil Kitchen', subtitle: 'Order #4821', progress: 0 });
await updateActivity({ progress: 0.6, status: 'Courier is on the way' });
await endActivity({ finalStatus: 'Delivered' });

One payload, three surfaces: the Dynamic Island / Lock Screen Live Activity on iOS 16.1+, an ongoing NotificationCompat progress card on Android, and a warn-once in-memory fallback when neither is available (Expo Go, web, old iOS, denied notification permission).

Install

npm install react-native-dynamic-island-telemetry
# or
yarn add react-native-dynamic-island-telemetry

Peer dependencies: react (18 or 19) and react-native (>= 0.72). The package itself has zero runtime dependencies and ships ESM + CJS builds with bundled type declarations.

Native setup (required for real system UI)

The npm package is pure JS. To render actual system UI you add a small native module named DynamicIslandTelemetry to your app — reference implementations and step-by-step guides ship inside the package:

  • iOSnative-setup/ios/README.md: copy TelemetryModule.swift, add the RCT_EXTERN_MODULE shim, create a Live Activity widget extension, and set NSSupportsLiveActivities: YES in Info.plist.
  • Androidnative-setup/android/README.md: copy TelemetryModule.kt, register the ReactPackage, declare and request POST_NOTIFICATIONS (API 33+), notification channel included.

Without native setup everything still works — the hook just routes to the JS fallback and no system UI appears.

Quickstart

import { useLiveTelemetry } from 'react-native-dynamic-island-telemetry';

function DeliveryTracker() {
  const { startActivity, updateActivity, endActivity, isActive, isSupported, backend } =
    useLiveTelemetry();

  const track = async () => {
    await startActivity({ title: 'Order #4821', subtitle: 'Thai Basil Kitchen', progress: 0 });
    await updateActivity({ progress: 0.45, status: 'Courier picked up your order' });
    // ... stream more updates ...
    await endActivity({ finalStatus: 'Delivered' });
  };

  return (
    <Button
      title={isActive ? 'Tracking…' : `Track order (${backend})`}
      onPress={track}
      disabled={isActive}
    />
  );
}

A complete simulated food-delivery screen (order stages streaming progress 0 → 1 with an in-app progress bar mirroring the system UI) lives in example/FoodDeliveryScreen.tsx.

API

useLiveTelemetry(): UseLiveTelemetryResult

| Member | Type | Description | | --- | --- | --- | | startActivity | (payload: StartActivityPayload) => Promise<string> | Validates + sanitizes the payload, starts the activity, resolves with an opaque activity id. Starting while another activity is active ends the previous one first (with a warning). | | updateActivity | (payload: UpdateActivityPayload) => Promise<void> | Pushes new progress/status. Called before startActivity() → warned no-op. Called while a start is still resolving → queued and flushed in order once the id lands. | | endActivity | (payload?: EndActivityPayload) => Promise<void> | Ends the running activity, optionally with a final status. Warned no-op when nothing is active. | | isActive | boolean | true while an activity started by this hook instance is live. | | isSupported | boolean | true once the native module is linked and its isSupported() resolved true. Settles asynchronously after mount. | | backend | 'ios-live-activity' \| 'android-notification' \| 'fallback' | Which surface renders the activity. Starts as 'fallback', settles after the native probe. |

The hook automatically ends a still-active activity on unmount.

Payload contract

All payloads are validated before crossing the bridge; a malformed payload throws a typed TelemetryPayloadError (with a code) and never reaches the native side. Unknown keys are stripped; progress is clamped.

| Payload | Field | Type | Rules | | --- | --- | --- | --- | | StartActivityPayload | title | string | required, non-empty | | | subtitle | string? | optional | | | progress | number | required; clamped to 0..1, NaN → 0 | | UpdateActivityPayload | progress | number? | optional; clamped to 0..1, NaN → 0 | | | status | string? | optional | | EndActivityPayload | finalStatus | string? | optional (whole payload may be omitted) |

TelemetryPayloadError.code is one of INVALID_PAYLOAD, INVALID_TITLE, INVALID_SUBTITLE, INVALID_PROGRESS, INVALID_STATUS, INVALID_FINAL_STATUS.

Other exports

  • clampProgress(progress) — the 0..1 clamp used internally.
  • validateStartPayload / validateUpdatePayload / validateEndPayload — the sanitizers, exported for advanced use.
  • TelemetryNativeModule — the rigid TypeScript contract the native module must fulfil (startActivity, updateActivity, endActivity, isSupported).
  • getTelemetryNativeModule() / NATIVE_MODULE_NAME — the resolver for NativeModules.DynamicIslandTelemetry (returns null when not linked).
  • fallbackTelemetryModule — the JS fallback backend (same interface), for manual wiring.
  • TELEMETRY_BACKENDS, TelemetryBackend, payload types.

Fallback behavior matrix

| Environment | backend | isSupported | What happens | | --- | --- | --- | --- | | iOS ≥ 16.1, module linked, Live Activities enabled | ios-live-activity | true | Dynamic Island + Lock Screen Live Activity | | iOS < 16.1, or Live Activities disabled in Settings | fallback | false | Native isSupported()false; in-memory JS state, warns once | | Android, module linked, notifications permitted | android-notification | true | Ongoing notification progress card | | Android, POST_NOTIFICATIONS denied / notifications off | fallback | false | Native isSupported()false; JS fallback | | Expo Go (no custom native code) | fallback | false | Module not linked; JS fallback, warns once | | Web / macOS / Windows / tests | fallback | false | JS fallback |

The fallback keeps the full lifecycle working (ids look like fallback-<n>), logs a single console.warn on first use, and renders no system UI — your in-app UI (e.g. the example's progress bar) keeps working unchanged.

License

MIT © Dinesh Kumar