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

@rocapine/rn-social-share

v0.1.0

Published

Headless social-sharing engine for React Native / Expo apps: capture a view as an image and share it across modular channels (Instagram Stories, TikTok, WhatsApp, SMS, system sheet, save to library, copy link).

Readme

@rocapine/rn-social-share

A headless social-sharing engine for React Native / Expo apps. It captures a view as an image and shares it across modular channels (Instagram Stories, TikTok, WhatsApp, SMS, the system share sheet, save-to-library, copy-link), behind a small abstraction so the app never touches react-native-share / the OpenSDKs directly.

Extracted from the sharing features in Eve (weekly baby card) and Unchaind (invite card). This is the shared plumbing — the visual card, the UI chrome, analytics wiring and i18n stay in each app and are injected.

What's in the box

src/
  types.ts          SharePayload · ShareOutcome · ShareChannel · ShareEvent · Sharer
  capture.ts        captureCardImage(ref, opts)   — view-shot → branded PNG
  createSharer.ts   createSharer({channels, onEvent, fallbackChannelId})
  channels/         systemSheet · whatsapp · copyLink · instagramStories · sms · tiktok · saveToLibrary
  react/            useShareCard · useScreenshotTrigger · areAllImagesLoaded · screenshot gate
  plugin/           withSocialShare  — iOS LSApplicationQueriesSchemes + Android <queries>

Design principle — headless first. The package owns capture, channel routing, screenshot detection and native query-permissions. It ships no UI: the two apps' share surfaces are very different (a Tamagui bottom sheet vs. a two-theme custom hub), so UI stays in the app.

Install (peer deps)

Required in the host app:

react-native-share  react-native-view-shot  expo-file-system

Optional — only needed if you register the matching channel/hook:

| Dependency | Needed by | | ----------------------------- | ------------------------------ | | expo-clipboard | copyLink | | expo-media-library | saveToLibrary | | expo-screen-capture | useScreenshotTrigger | | tiktok-opensdk-react-native | tiktok | | @expo/config-plugins | withSocialShare (build-time) |

Optional channels lazy-require their native module, so importing the barrel never forces a dependency you don't use.

Usage

1. Build a sharer (once, near your feature)

import {
  createSharer,
  systemSheet,
  copyLink,
  sms,
  whatsapp,
  instagramStories,
  tiktok,
} from "@rocapine/rn-social-share";

export const inviteSharer = createSharer({
  channels: [
    copyLink(),
    sms(),
    whatsapp(),
    instagramStories({ appId: FACEBOOK_APP_ID }),
    tiktok({ onError: (e) => logger.warn("tiktok", e) }),
    systemSheet(), // default fallback (id "system-sheet")
  ],
  onEvent: (e) => {
    if (e.type === "share_completed") {
      analytics.logEvent("invite_link_shared", {
        channel: e.channel,
        fell_back: e.fellBack,
      });
    }
  },
});

Add or remove a channel = edit the array. That's the whole extension surface.

2. Capture the card and share

import { useShareCard } from "@rocapine/rn-social-share";

const { ref, capture } = useShareCard({ fileName: "unchaind-invite.png" });

const onChannel = async (channelId: string) => {
  const imageUri = channelId === "link" ? undefined : await capture();
  await inviteSharer.share(channelId, {
    message,
    link,
    imageUri,
    fileName: "unchaind-invite",
  });
};

// Render the card off-screen; point the ref at it:
<View style={{ position: "absolute", left: -10000 }} pointerEvents="none">
  <YourShareCard ref={ref} inviteCode={code} />
</View>;

Need a rounded variant for the sheet and a square full-bleed one for Stories? Call useShareCard twice, or call captureCardImage(ref, opts) directly with each ref.

3. Share on screenshot (optional, the Expo Marathon pattern)

import { useScreenshotTrigger } from "@rocapine/rn-social-share";

useScreenshotTrigger(isVisible && !isSheetOpen, () =>
  openShareSheet("screenshot"),
);

A shared, app-wide gate dedupes the iOS double-fire / multiple-listener case (ROC-2854).

4. Wire native query-permissions (build time)

// app.json / app.config.ts plugins
[
  "@rocapine/rn-social-share",
  { "channels": ["instagram", "whatsapp", "tiktok"] },
]

Adds the iOS LSApplicationQueriesSchemes and Android <queries> entries the channels' isAvailable checks need. Scope: query/visibility only — TikTok's full native wiring (client key, FileProvider) still lives in the app's withTikTokShare plugin for now.

Authoring a channel

import type { ShareChannel } from "@rocapine/rn-social-share";

export const threads = (opts: { id?: string } = {}): ShareChannel => {
  const id = opts.id ?? "threads";
  return {
    id,
    isAvailable: async (p) => Boolean(p.imageUri),
    share: async (p) => {
      /* ...open Threads... */
      return { completed: true, channel: id };
    },
  };
};

createSharer handles routing, availability checks, fallback and events around it.

Consuming this package

Consumed as a git dependency pinned to a tag (no registry). In the host app's package.json:

"@rocapine/rn-social-share": "github:Rocapine/rn-social-share#v0.1.0"

The built dist/ (CJS + ESM + .d.ts) is committed, so installs work with any package manager without an install-time build step. Then declare the peer deps you actually use and add the config plugin (see above).

Development

npm install
npm run type    # tsc --noEmit
npm test        # jest
npm run build   # tsup → dist/ (CJS + ESM + d.ts)

CI (GitHub Actions) runs type-check + tests + build on every push/PR and fails if the committed dist/ is stale. Rebuild and commit dist/ before tagging a release.

Status & next steps

  • [x] Core: types, capture, createSharer, channels, React hooks, config plugin
  • [x] Tests for createSharer, the screenshot gate, and channel translation (24 passing)
  • [x] Build (tsup) + type-check + CI, published as v0.1.0
  • [ ] Migrate Unchaind's components/invite/ onto the package (superset of channels)
  • [ ] Migrate Eve's features/Sharing/ (adds saveToLibrary + screenshot trigger usage)
  • [ ] Fold TikTok's full native config into withSocialShare