Respectlytics Respect lytics
Menu
React Native Video completion Privacy-first

How to track video completions in React Native without personal data

Video engagement events — play, 25% / 50% / 75% milestones, complete — are dense signals for content apps. Most analytics SDKs default to tagging them with the video ID, content category, and watch duration, building a per-user watch history along the way. Respectlytics helps developers avoid collecting personal data in the first place: in React Native, each milestone is its own named event, with no metadata. Below: how to wire to the player's progress callbacks, why milestones beat raw position, and what content metadata stays in your CMS.

Fire the call at meaningful playback milestones: video_play_started, video_25pct_reached, video_50pct_reached, video_75pct_reached, video_completed. Don't pass video ID, content title, or duration.

Install the React Native SDK

bash Respectlytics
npm install @respectlytics/react-native
# or
yarn add @respectlytics/react-native

JavaScript-only — no native modules, no auto-linking, no New Architecture migration concerns. Bundle size: ~14KB minified+gzipped. Works in any Expo project (managed or bare) without expo prebuild.

Initialize Respectlytics in React Native

js Respectlytics
// App.tsx (or App.js)
import { useEffect } from 'react';
import Respectlytics from '@respectlytics/react-native';

export default function App() {
  useEffect(() => {
    Respectlytics.configure({ appKey: '<YOUR_APP_KEY>' });
  }, []);
  return <YourApp />;
}

Initialize once in your top-level component. No native config; no Info.plist or AndroidManifest changes. The SDK is Hermes- and JSC-compatible.

Track the event in React Native

js Respectlytics
import Respectlytics from '@respectlytics/react-native';
import { useState } from 'react';
import Video from 'react-native-video';

export function TrackedVideo({ source }) {
  const [milestones, setMilestones] = useState({});
  return (
    <Video
      source={source}
      onProgress={({ currentTime, playableDuration }) => {
        const p = currentTime / playableDuration;
        const next = { ...milestones };
        if (p >= 0.25 && !milestones.m25) { Respectlytics.track('video_25pct_reached'); next.m25 = true; }
        if (p >= 0.50 && !milestones.m50) { Respectlytics.track('video_50pct_reached'); next.m50 = true; }
        if (p >= 0.75 && !milestones.m75) { Respectlytics.track('video_75pct_reached'); next.m75 = true; }
        if (p >= 0.99 && !milestones.complete) { Respectlytics.track('video_completed'); next.complete = true; }
        if (Object.keys(next).length !== Object.keys(milestones).length) setMilestones(next);
      }}
    />
  );
}

Reset milestones to {} when the source changes via the source prop. onProgress fires several times per second — the gating logic is essential.

Privacy & implementation notes

Tracking exact playback position generates 30+ events per minute of viewing and dilutes every other signal in your analytics pipeline. Four milestones (25, 50, 75, 100%) per video carry essentially the same product signal at 1/100th the volume.

Your CMS already stores video titles, descriptions, durations, and content categories. Joining video_completed_movie events to your CMS's view-count-per-video data answers most product questions; storing the title as analytics metadata duplicates a system of record without adding signal.

The React Native SDK is JavaScript-only — no Objective-C/Swift bridging on iOS, no Java/Kotlin bridging on Android. Side effects: no react-native link, no auto-linking, no New Architecture migration concerns, no platform-channel exception surfaces. Trade-off: no access to platform-only metadata (which we don't want to collect anyway).

Works in Expo managed workflow without expo prebuild. No config plugin is required. EAS Build users: nothing to configure. This is the smoothest integration path on RN — most analytics SDKs require ejecting from managed.

How this compares to other analytics SDKs

Video completion eventFirebase AnalyticsMixpanelRespectlytics
Video ID as parameterRecommendedRecommendedForbidden (use top-N event_names)
Content category / genreRecommendedRecommendedUse distinct event_name (top-N)
Watch duration in secondsRecommendedRecommendedOut of scope (use milestones)
Per-user watch historyYesYesOut of scope (use content backend)
Completion *rate* by country / platformYesYesYes (default)

Frequently asked questions

How do we know which videos are most-watched?

Your content backend already has play counts per video — that's where video-level engagement lives. Respectlytics is for product engagement at the feature level ("is the video player surface working?"). Conflating them duplicates a system of record.

Should we track exact playback position?

No — milestones (25 / 50 / 75 / 100%) carry the signal. Exact position produces a flood of low-value events that drown the milestone signal. Player-side analytics (your video player vendor's SDK) has its own per-second tracking if you genuinely need it.

What about content category breakdowns (movies, shows, shorts)?

If you have a small fixed set, distinct event names: video_completed_movie, video_completed_show. For high-cardinality categorization, use your content backend's analytics — it has the metadata you need.

Does this work for live streams?

Conceptually different. Use distinct event names — livestream_joined, livestream_left — and fire on join and leave. Milestones don't apply to live; duration is computed from join → leave timestamp difference.

Related guides

Track what matters. Collect nothing you don't.

Five-field event schema, RAM-only event queue, no IDFA, no AAID, no persistent user IDs. Helps developers avoid collecting personal data in the first place.