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

@tryonit/react-native

v0.2.0

Published

Virtual try-on for React Native and Expo (Expo Go, dev builds and bare apps): makeup, beauty filters, face stickers, glasses, hats, earrings, watches, rings and hair color on the phone camera. Same asset manifests as TryOnIt for the web. Free, open source

Readme

@tryonit/react-native

Virtual try-on for React Native and Expo. Let shoppers see lipstick, beauty filters, face stickers, glasses, sunglasses, hats, earrings, watches, rings and hair color on themselves with the phone camera. One package for Expo Go, Expo dev builds and bare React Native, using the same product manifests as TryOnIt for the web. Free, open source (MIT), and private: camera frames are processed on the device and never uploaded.

import { TryOnButton } from '@tryonit/react-native';

<TryOnButton asset="https://cdn.example.com/tryon/aviator.json" />;

How it works

The package renders a react-native-webview that runs the TryOnIt engine (MediaPipe face, hand and hair tracking, WebGL makeup shaders, three.js for 3D products). The TryOnIt code is embedded in the package. MediaPipe and three.js are loaded lazily from a CDN the first time they are needed (three.js only for 3D products), then cached by the WebView. Your app talks to the camera view through a typed message bridge: props, a ref API, and events.

Because it only needs react-native-webview, it works in Expo Go without a custom native build.

Requirements

| | Minimum | | -------------------- | --------------------------------------------------------------------------------------- | | iOS | 16.4 (WebKit import maps and WebAssembly SIMD) | | Android | 8.0 with an up to date Android System WebView (Chrome 111 or newer) | | React Native | 0.73 or newer | | Expo | SDK 50 or newer (tested with SDK 57) | | react-native-webview | 13 or newer | | Network | Needed the first time to load MediaPipe, models and three.js, unless you self host them |

Install

Expo (Expo Go or dev builds)

npx expo install @tryonit/react-native react-native-webview

Expo Go already includes the WebView and the camera permission, so you can try it immediately with npx expo start.

For development builds and store builds, add the config plugin to app.json so the camera permission is added to your native projects:

{
  "expo": {
    "plugins": [
      [
        "@tryonit/react-native",
        {
          "cameraPermission": "Allow $(PRODUCT_NAME) to use the camera so you can try products on."
        }
      ]
    ]
  }
}

Then rebuild: npx expo prebuild or eas build.

Bare React Native

npm install @tryonit/react-native react-native-webview
cd ios && pod install

iOS Info.plist:

<key>NSCameraUsageDescription</key>
<string>Allow the camera so you can try products on.</string>

Android AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA" />

Quick start

1. Drop in button

import { TryOnButton } from '@tryonit/react-native';

export function ProductScreen() {
  return (
    <TryOnButton
      asset="https://cdn.jsdelivr.net/gh/ArbabNaseer82/try-it-on@main/samples/glasses-aviator.json"
      title="Aviator Sunglasses"
      theme={{ colors: { primary: '#0f766e' }, radius: 12 }}
      onCapture={(photo) => console.log(photo.width, photo.height)}
      onError={(error) => console.warn(error.code, error.message)}
    />
  );
}

The button asks for camera permission on Android, then opens a full screen modal (a page sheet on iOS) with the camera, a shutter, a camera switch, a photo upload fallback and status messages such as "Look at the camera".

2. Inline view

import { TryOnView } from '@tryonit/react-native';

const lipstick = {
  version: 1,
  id: 'ruby',
  type: 'makeup.lips',
  color: '#B0123A',
  finish: 'satin',
  variants: [
    { id: 'ruby', name: 'Ruby', swatch: '#B0123A' },
    { id: 'nude', name: 'Nude', swatch: '#C48A7A', overrides: { color: '#C48A7A' } },
  ],
} as const;

<TryOnView asset={lipstick} style={{ flex: 1 }} />;

3. Your own UI (headless)

import { useState } from 'react';
import { Button, Image, View } from 'react-native';
import { TryOnView, useTryOn, type TryOnPhoto } from '@tryonit/react-native';

export function CustomTryOn() {
  const tryOn = useTryOn();
  const [photo, setPhoto] = useState<TryOnPhoto | null>(null);

  return (
    <View style={{ flex: 1 }}>
      <TryOnView {...tryOn.viewProps} controls="none" asset={lipstick} />
      <Button title="Nude" onPress={() => tryOn.setVariant('nude')} />
      <Button title="Flip camera" onPress={() => tryOn.switchCamera()} />
      <Button title="Snap" onPress={async () => setPhoto(await tryOn.capture())} />
      {photo && <Image source={{ uri: photo.dataUrl }} style={{ width: 120, height: 160 }} />}
    </View>
  );
}

tryOn.state mirrors the camera session: status, error, asset, variantId, intensity, camera, tracking.faceVisible, tracking.handVisible, perf.

Products and filters

Products are the same JSON asset manifests as on the web:

| You want | type | File you provide | | ------------------------------------------------------- | --------------------------------------------- | ------------------------------- | | Lipstick, blush, eyeshadow, eyeliner, brows, foundation | makeup.* | none, only colors | | Beauty filter (complete makeup look) | makeup.look | none | | Hair color | hair.color | none | | Fun face filter, sticker, mask | face.overlay2d | PNG with transparent background | | Glasses, hats, earrings, watches, rings | glasses, hat, earrings, watch, ring | GLB 3D model in millimeters |

React Native specifics:

  • Pass a manifest object, an absolute https:// URL to a manifest, or an async loader ((signal) => fetch(...).then((r) => r.json())). Relative URLs such as /assets/x.json do not exist in React Native.
  • Files referenced from a manifest loaded by URL may be relative to that URL ("model": "aviator.glb"). Files in manifest objects must be absolute URLs.
  • Files on your CDN need CORS (Access-Control-Allow-Origin: *).
  • Validate before shipping: npx -p @tryonit/core tryonit-validate ./tryon.

Full recipes (beauty filters, stickers, 3D models, variants, hosting, Shopify and CMS loading): Creating products and filters. Field reference: MANIFEST_SPEC.md.

Ready made samples you can use while developing (served by jsDelivr from GitHub):

https://cdn.jsdelivr.net/gh/ArbabNaseer82/try-it-on@main/samples/lipstick.json
https://cdn.jsdelivr.net/gh/ArbabNaseer82/try-it-on@main/samples/look.json
https://cdn.jsdelivr.net/gh/ArbabNaseer82/try-it-on@main/samples/hair.json
https://cdn.jsdelivr.net/gh/ArbabNaseer82/try-it-on@main/samples/glasses-aviator.json
https://cdn.jsdelivr.net/gh/ArbabNaseer82/try-it-on@main/samples/glasses-round.json
https://cdn.jsdelivr.net/gh/ArbabNaseer82/try-it-on@main/samples/earrings.json
https://cdn.jsdelivr.net/gh/ArbabNaseer82/try-it-on@main/samples/moustache.json

API

<TryOnView>

| Prop | Type | Default | Description | | ------------------- | ---------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | asset | AssetSource \| null | | Manifest object, absolute URL or async loader. Changing it swaps the product. | | controls | 'web' \| 'none' | 'web' | Built in shutter, camera switch, photo upload and messages, or camera only. | | autoStart | boolean | true | Open the camera when ready. | | engineOptions | EngineOptions | | modelBaseUrl, wasmBaseUrl, models, camera { facing, width, height }, performance, mirror, fit, debug, delegate, smoothing, irisDiameterMm, three. | | theme | ThemeInput | | colors (primary, onPrimary, surface, onSurface, overlay, danger, success, muted), radius, fontFamily, fontSize. Same tokens as @tryonit/react. | | labels | Partial<TryOnLabels> | English | Texts of the built in controls (capture, switchCamera, uploadPhoto, retry, loading, noFace, noHand, noBody, cameraDenied, ...). | | captureOptions | { type?, quality?, mirror? } | JPEG, 0.9 | Default photo format. | | cdn | { mediapipeUrl?, threeBaseUrl?, importMap? } | jsDelivr | Self host MediaPipe and three.js. | | pauseInBackground | boolean | true | Pause tracking while the app is in the background. | | style | ViewStyle | flex: 1 | | | webViewProps | WebViewProps | | Extra WebView props (advanced). | | onReady | () => void | | The camera page is loaded. | | onStateChange | (state: TryOnState) => void | | Every state change. | | onStatusChange | (status) => void | | idle, checking, requesting-camera, loading-models, ready, running, paused, error. | | onError | (error: TryOnErrorInfo) => void | | { code, message, retryable, canUsePhotoFallback }. | | onAssetLoaded | (asset) => void | | A product finished loading. | | onCapture | (photo: TryOnPhoto) => void | | Photos taken with the built in shutter. | | onLog | (level, message) => void | | Diagnostics from the page. |

Ref methods (ref on TryOnView, or useTryOn())

| Method | Description | | ------------------------------ | ------------------------------------------------------- | | start(), stop() | Open or release the camera. | | pause(), resume() | Pause tracking. | | setAsset(source) | Swap the product. Resolves with the validated manifest. | | setVariant(id) | Shade, color or style. | | setIntensity(0..1) | Makeup and hair strength. | | switchCamera() | Front and rear camera. | | setCompare(position \| null) | Before and after split, 0..1 from the left. | | setDebug(enabled) | Landmark overlay and FPS HUD. | | capture(options?) | Promise<TryOnPhoto>. | | getState() | Latest TryOnState. |

TryOnPhoto

{
  dataUrl: string;
  base64: string;
  mimeType: string;
  width: number;
  height: number;
}

dataUrl works directly in <Image source={{ uri: photo.dataUrl }} />. Save or share it, for example with Expo:

import { File, Paths } from 'expo-file-system';
import * as Sharing from 'expo-sharing';

const file = new File(Paths.cache, 'try-on.jpg');
file.write(photo.base64, { encoding: 'base64' });
await Sharing.shareAsync(file.uri);

<TryOnButton> and <TryOnModal>

TryOnButton accepts every TryOnView prop plus label, children, buttonStyle, textStyle, disabled, title, closeLabel, requestPermission (default true) and onOpenChange. TryOnModal accepts every TryOnView prop plus visible, onClose, title, closeLabel, headerStyle.

Other exports

requestCameraPermission(), resolveNativeAsset(), validateManifest(), safeValidateManifest(), ErrorCode, TryOnError, themeToCssVars(), createTryOnHtml() and createImportMap() (advanced: host the page yourself), RUNTIME_VERSIONS, and all types.

Self hosting (no third party requests)

<TryOnView
  asset={asset}
  engineOptions={{
    modelBaseUrl: 'https://cdn.my-shop.com/tryon/models',
    wasmBaseUrl: 'https://cdn.my-shop.com/tryon/wasm',
  }}
  cdn={{
    mediapipeUrl: 'https://cdn.my-shop.com/tryon/mediapipe/vision_bundle.mjs',
    threeBaseUrl: 'https://cdn.my-shop.com/tryon/three',
  }}
/>

Copy @mediapipe/tasks-vision (vision_bundle.mjs and wasm/), the four model files and the three package (build/ and examples/) to your CDN with CORS enabled. RUNTIME_VERSIONS tells you which versions this build expects.

Privacy

Camera frames stay on the device. TryOnIt sends no analytics. The only network requests are the library, model and product files listed above. Photos leave the device only if your code sends them.

Troubleshooting

| Problem | Fix | | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Black view, nothing loads | Check the device: iOS 16.4 or newer, Android System WebView up to date. Pass onLog to see page errors. | | CAMERA_DENIED | The user refused the permission. The built in UI offers a photo upload. On Android call requestCameraPermission() before showing the view (TryOnButton does this). | | Works in Expo Go but not in your build | Add the config plugin (or the Info.plist and AndroidManifest entries) and rebuild. | | Product does not load | Use absolute https:// URLs, enable CORS on your CDN, and run npx -p @tryonit/core tryonit-validate. | | Android emulator shows no face | Configure the emulator camera to use your webcam, or test on a real device. | | Slow on older phones | engineOptions={{ performance: 'battery' }}. |

Limits and roadmap

This package runs the engine in a WebView: it works everywhere React Native runs, including Expo Go, and shares 100% of its behavior with the web packages. A fully native engine (VisionCamera, native MediaPipe, Skia and Filament) is on the roadmap, behind the same API.

MIT licensed.