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-tinykit

v0.11.0

Published

A lightweight React Native toolkit for iOS with native utilities and optional SPIndicator, SPAlert and SPConfetti overlays

Readme

react-native-tinykit

npm version license

A modular collection of native iOS utilities for React Native's New Architecture. TinyKit brings system APIs, status overlays, and small everyday interactions to TypeScript, with native features you can include individually.

Features

| Feature | What it does | | ------------------------------ | ------------------------------------------------------------- | | Restart | Reload the React Native application | | UUID | Generate random UUID version 4 strings | | ThermalState | Read and monitor the device's thermal state | | Review | Request an App Store review | | KeepAwake | Prevent automatic screen locking | | Haptics | Trigger impact, selection, and notification feedback | | ColorPicker | Present the native iOS color picker | | Mail | Compose mail with attachments or open a mail app | | Toast | Show compact SPIndicator notifications | | Alert | Show SPAlert status overlays and loading indicators | | Confetti | Start and stop SPConfetti celebrations | | Translation | Present Apple's translation panel and return replacement text |

Installation · Quick start · Example app · Migration

Philosophy

TinyKit keeps its scope focused: native iOS capabilities, small JavaScript APIs, and explicit control over what your app compiles.

| Design choice | How TinyKit applies it | | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Native integration | TurboModules connect TypeScript to iOS implementations. System features use native APIs; Translation uses Apple's SwiftUI presentation API. | | Runtime dependencies | No additional JavaScript runtime dependencies beyond react and react-native. No Expo or Nitro Modules runtime is required. | | Selective inclusion | Named exports let your bundler handle JavaScript tree shaking when enabled. An explicit native feature list excludes unused implementations and their framework requirements. | | Optional third-party UI | Toast, Alert, and Confetti use the original SPIndicator, SPAlert, and SPConfetti libraries. Their Pods are installed only when selected. | | Focused support | iOS 17+ and the New Architecture. Android, web, and the legacy bridge are not supported targets. |

To reduce native build size, configure react-native-tinykit.features. All native features are enabled by default. The shared TurboModule core and Codegen remain in the build; size savings come from excluding unused native implementations and dependencies. Final app size also depends on bundler and linker settings.

Requirements

  • React Native with the New Architecture enabled
  • iOS 17 or newer
  • react and react-native as peer dependencies

The optional Translation feature requires a physical iPhone or iPad running iOS 17.4 or later. Check Translation.isSupported() before presenting it; the simulator and Mac Catalyst are not supported by this feature.

Installation

npm install react-native-tinykit
# or: yarn add react-native-tinykit

Choose your native features, configure the overlay Pods if needed, then run pod install and rebuild the iOS app.

Optional native feature selection

List the features you use in your app's package.json:

{
  "react-native-tinykit": {
    "features": ["Haptics", "KeepAwake"]
  }
}

Use the feature names from the catalog, with the same spelling and capitalization. Omitting the configuration enables all features; an empty features array compiles only the TurboModule core. Run pod install and rebuild whenever the list changes.

Most APIs throw or reject with a missing-feature error when their native implementation is excluded. canSendMail() returns false and openMail() falls back to mailto: when Mail is excluded. Translation.isSupported() returns false when Translation is excluded.

iOS setup

If Toast, Alert, or Confetti is enabled, including with the default configuration, load the Podfile helper and call it inside your app target, before use_native_modules!:

# Resolve from the app, including in hoisted/monorepo installations.
require Pod::Executable.execute_command('node', ['-p',
  "require.resolve('react-native-tinykit/scripts/overlay_pods.rb', { paths: [process.argv[1]] })",
  __dir__
]).strip

target 'YourApp' do
  tinykit_overlay_pods!(app_path: File.expand_path('..', __dir__))
  config = use_native_modules!
  # Keep your existing use_react_native! and post_install configuration.
end

The helper reads the same feature list and installs only the selected overlay Pods. Omit it when all three overlay features are excluded, as in the Haptics / KeepAwake configuration above.

cd ios && pod install

The Podfile helper pins the original upstream Git releases. Their podspec versions differ from their source tags:

| Feature | Library | Git tag | Podspec version | | ---------- | ----------- | ------- | --------------- | | Toast | SPIndicator | 1.6.5 | 1.6.4 | | Alert | SPAlert | 5.1.9 | 5.1.8 | | Confetti | SPConfetti | 1.4.2 | 1.4.0 |

The older version labels in Podfile.lock are expected. CHECKOUT OPTIONS records the actual source tags. The helper is needed to obtain these pinned sources instead of the CocoaPods registry releases. No upstream Swift code is copied or modified.

JavaScript imports

Recommended: use named imports from the package root:

import { impact, useKeepAwake, Translation } from 'react-native-tinykit';

The package provides ES module exports, so bundlers with tree shaking enabled can automatically remove unused JavaScript exports. Tree shaking depends on your bundler and build configuration; feature-specific import paths are optional:

import { impact } from 'react-native-tinykit/haptics';

JavaScript tree shaking does not select native source files. Configure react-native-tinykit.features separately to control native compilation.

Quick start

With Haptics enabled and the app rebuilt:

import { Button } from 'react-native';
import { impact } from 'react-native-tinykit';

export default function App() {
  return <Button title="Try haptics" onPress={() => impact('light')} />;
}

Restart

restart(): void triggers a reload of the React Native application, for example after applying a configuration change. It does not relaunch the iOS process or clear persisted data.

import { restart } from 'react-native-tinykit';

restart();

UUID

uuid(): string synchronously returns a random RFC 4122 version 4 UUID generated by Foundation's NSUUID.UUID.UUIDString. Add UUID to react-native-tinykit.features when using an explicit feature list.

import { uuid } from 'react-native-tinykit';

const identifier = uuid();

Thermal State

Read the current state synchronously, or subscribe to changes:

import { getThermalState, onThermalStateChange } from 'react-native-tinykit';

const state = getThermalState();

const subscription = onThermalStateChange((nextState) => {
  console.log('Thermal state:', nextState);
});

// Remove the listener when it is no longer needed.
subscription.remove();

| API | Returns | | -------------------------------- | -------------------- | | getThermalState() | ThermalState | | onThermalStateChange(listener) | { remove(): void } |

ThermalState is 'nominal' | 'fair' | 'serious' | 'critical', from normal conditions to the highest thermal pressure. Use it to adjust resource-intensive work. Native monitoring starts with the first listener and stops when the last listener is removed.

App Review

requestReview(): Promise<void> asks StoreKit to present an App Store review prompt:

import { requestReview } from 'react-native-tinykit';

await requestReview();

The Promise resolves after the request is submitted, without reporting whether a prompt appeared or a review was submitted. The system controls presentation; follow Apple's review request guidance when choosing when to call it.

Keep Awake

Use the hook to prevent automatic screen locking while a screen is mounted:

import { Text } from 'react-native';
import { useKeepAwake } from 'react-native-tinykit';

function ReadingScreen() {
  useKeepAwake();
  return <Text>The screen stays awake while this view is mounted.</Text>;
}

| API | Behavior | | ---------------------- | ---------------------------------------------------------------- | | activate(): void | Disable automatic screen locking | | deactivate(): void | Allow automatic screen locking again | | useKeepAwake(): void | Activate on mount and deactivate on unmount | | <KeepAwake /> | Render nothing and apply the same lifecycle behavior as the hook |

These APIs change the app-wide idle timer. They are not reference counted: unmounting one keep-awake consumer can deactivate another. Use a single owner when multiple screens need to coordinate this state.

Haptics

Trigger native impact, selection, and notification feedback:

import { impact, selection, notification } from 'react-native-tinykit';

impact('light');
selection();
notification('success');

| API | Accepted values | | -------------------------- | ----------------------------------------------------- | | impact(style): void | 'light', 'medium', 'heavy', 'soft', 'rigid' | | selection(): void | No arguments | | notification(type): void | 'success', 'warning', 'error' |

The corresponding TypeScript types are ImpactFeedbackStyle and NotificationFeedbackType.

Color Picker

showColorPicker(options?): Promise<ColorPickerResult> presents the native UIColorPickerViewController and returns the selected color when it closes.

import { showColorPicker } from 'react-native-tinykit';

const result = await showColorPicker({
  selectedColor: '#007AFF',
  supportsAlpha: true,
  title: 'Pick a color',
  showDoneButton: true,
  detents: [
    { type: 'custom', identifier: 'compact', height: 420 },
    { type: 'large' },
  ],
  selectedDetentIdentifier: 'compact',
  prefersGrabberVisible: true,
});

console.log(result.color); // #RRGGBBAA

| Option | Type | Description | | --------------------------------- | --------------------- | -------------------------------------------------------------------------------- | | selectedColor | string | Initial color. Supports #RGB, #RGBA, #RRGGBB, and #RRGGBBAA. | | supportsAlpha | boolean | Shows the alpha slider. Defaults to true. | | supportsEyedropper | boolean | Enables eyedropper support when available on the OS. | | maximumLinearExposure | number | Maximum linear exposure when available on the OS. | | title | string | Optional picker title. | | showDoneButton | boolean | Shows a top-right Done button. | | doneButtonTitle | string | Custom title for the Done button. | | detents | ColorPickerDetent[] | Sheet detents. Supports medium/large and custom height/fraction detents. | | selectedDetentIdentifier | string | Initially selected detent. Use medium, large, or a custom detent identifier. | | largestUndimmedDetentIdentifier | string | Largest detent that keeps the presenting view undimmed. | | prefersGrabberVisible | boolean | Shows the sheet grabber. |

type ColorPickerDetent = {
  type: 'medium' | 'large' | 'custom';
  identifier?: string;
  height?: number; // custom height in points
  fraction?: number; // custom fraction of maximum sheet height
};

The result contains a hex string and individual color channels:

type ColorPickerResult = {
  color: string; // #RRGGBBAA
  red: number;
  green: number;
  blue: number;
  alpha: number;
};

Mail

openMail(options?): Promise<MailResult> presents the native MFMailComposeViewController when the device is configured to send mail. canSendMail(): boolean lets you check availability first.

import { openMail } from 'react-native-tinykit';

const result = await openMail({
  subject: 'Feedback',
  recipients: ['[email protected]'],
  body: '<p>Hello from TinyKit</p>',
  isHTML: true,
});

console.log(result); // 'sent', 'saved', 'cancelled', or 'opened'

| Option | Type | Description | | --------------- | ------------------ | ---------------------------------------- | | subject | string | Initial email subject. | | recipients | string[] | Initial To recipients. | | ccRecipients | string[] | Initial Cc recipients. | | bccRecipients | string[] | Initial Bcc recipients. | | body | string | Initial email body. | | isHTML | boolean | Whether body contains HTML. | | attachments | MailAttachment[] | Local files attached to the email draft. |

type MailAttachment = {
  path?: string; // Absolute local path; use either path or uri
  uri?: string; // Local file URI
  type?: string; // File extension or MIME type
  mimeType?: string; // Explicit MIME type; takes precedence over type
  name?: string; // File name shown in the composer
};

When the native composer is unavailable, including when Mail is excluded, openMail opens mailto: with the first recipient and returns 'opened'.

openMail rejects when another composer is already open, an attachment cannot be read, or the system cannot open the fallback URL. Attachments must use a local path or a file:// URI. The mailto: fallback only receives the first recipient; the remaining options and attachments are not forwarded.

Toast

Toast.show(options?): Promise<void> presents a compact SPIndicator toast:

import { Toast } from 'react-native-tinykit';

await Toast.show({
  title: 'Saved',
  message: 'Your changes are ready',
  icon: 'done',
  haptic: 'success',
});

| Option | Type | Default | | --------- | --------------------------------------------- | -------- | | title | string | '' | | message | string | '' | | icon | 'done' \| 'error' | 'done' | | haptic | 'success' \| 'warning' \| 'error' \| 'none' | 'none' |

Toast uses SPIndicator's default duration. Its public option type is ToastOptions. See overlay behavior for shared lifecycle and Promise semantics.

Alert

Alert.show(options?): Promise<void> presents an SPAlert status overlay. Use it for success states and progress indicators:

import { Alert } from 'react-native-tinykit';

await Alert.show({ title: 'Done', icon: 'heart', duration: 2000 });

// A persistent spinner, dismissed from a later action.
await Alert.show({ title: 'Loading', icon: 'spinner', duration: 0 });
await Alert.dismissAll();

| Option | Type | Default | | ---------- | --------------------------------------------- | -------- | | title | string | '' | | message | string | '' | | icon | 'done' \| 'error' \| 'spinner' \| 'heart' | 'done' | | haptic | 'success' \| 'warning' \| 'error' \| 'none' | 'none' | | duration | Finite number, in milliseconds | 2000 |

A duration of zero or less disables automatic dismissal, including for spinners. Alert.dismissAll(): Promise<void> dismisses all TinyKit alerts. The standalone dismissAll export is also available.

Alert is a status overlay, not a confirmation dialog. Alias the import if you also use React Native's Alert. Its public option type is AlertOptions.

Confetti

Start the original SPConfetti full-width downward animation, using arc, star, heart, and triangle particles:

import { Confetti } from 'react-native-tinykit';

await Confetti.start({ duration: 2000 });

// From a later action, stop emitting particles early.
await Confetti.stop();

Confetti.start(options?): Promise<void> accepts duration in milliseconds, defaulting to 2000. The value must be finite and non-negative. Starting again replaces the stop timer. Confetti.stop(): Promise<void> stops new particles; existing particles finish their animation. The public option type is ConfettiOptions.

Overlay behavior

Toast, Alert, and Confetti require the Podfile helper when enabled. Their methods resolve after the native presentation, dismissal, or animation request is made on the main thread, not after the animation finishes. Presentation rejects if no window is available; excluded features reject with a missing-feature error. Active overlays are cleaned up when the native module is invalidated.

Toast and Alert haptics use OverlayHaptic and do not require the separate Haptics feature.

Translation

Present Apple's system translation panel, with an optional action to return replacement text to your app:

import { Translation } from 'react-native-tinykit';

if (Translation.isSupported()) {
  const result = await Translation.present({
    text: 'Hello, world! 👋',
    allowsReplacement: true,
  });
  if (result.status === 'replaced') {
    console.log(result.translatedText);
  }
}

// From another action, close the active panel. Safe if none is open.
await Translation.dismiss();

| API | Behavior | | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | Translation.isSupported() | Returns false if the native feature is excluded, on the simulator, on Mac Catalyst, or before iOS 17.4 | | Translation.present(options) | Resolves when the panel closes: { status: 'dismissed' }, or { status: 'replaced', translatedText: string } when the user chooses Replace Translation | | Translation.dismiss() | Closes the current panel and resolves its pending present call with dismissed |

The equivalent named exports are isTranslationSupported, showTranslation and dismissTranslation.

| Option | Type | Description | | ------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | text | string | Required, non-empty text. Unicode, emoji and whitespace are preserved. | | allowsReplacement | boolean | Defaults to false. Offers Apple's Replace Translation action; your app decides how to use the returned text. | | targetViewNode | native view ref.current or React tag | Optional anchor for the iPad popover. The view must be mounted and visible. | | anchor | { x, y, width, height } | Alternative to targetViewNode: rectangle in window coordinates, in points. | | arrowEdge | 'top' \| 'bottom' \| 'leading' \| 'trailing' | Preferred popover arrow edge. Defaults to 'top'; the system may adjust placement. |

For example, attach the iPad popover to the text being translated:

import { useRef, type ComponentRef } from 'react';
import { Text, Button } from 'react-native';
import { Translation } from 'react-native-tinykit';

function TranslatableText({ text }: { text: string }) {
  const textRef = useRef<ComponentRef<typeof Text>>(null);
  return (
    <>
      <Text ref={textRef}>{text}</Text>
      <Button
        title="Translate"
        disabled={!Translation.isSupported()}
        onPress={() => {
          Translation.present({ text, targetViewNode: textRef.current }).catch(
            console.error
          );
        }}
      />
    </>
  );
}

Pass either anchor or targetViewNode, not both. Without an anchor, the popover uses the presenting view's center. An unmounted, empty, or offscreen target rejects with E_TRANSLATION_INVALID_ANCHOR. Only one panel may be open at a time; another call rejects with E_TRANSLATION_ALREADY_PRESENTED. Unsupported devices reject with E_TRANSLATION_UNAVAILABLE. Invalid text rejects with E_TRANSLATION_INVALID_TEXT; an unavailable presenter rejects with E_TRANSLATION_NO_PRESENTER. Invalid option values reject with E_TRANSLATION_INVALID_OPTIONS; module invalidation rejects a pending presentation with E_TRANSLATION_CANCELLED.

This uses Apple's public translationPresentation API. The user chooses languages in the system panel, which may ask to download language models. Closing or copying in the panel does not return translated text; only Replace Translation does. This API presents system UI and does not offer background or batch translation. Apple requires a real device to test translation (WWDC24).

Migration

From react-native-ios-translation

Use the Translation API from the package root:

import { Translation } from 'react-native-tinykit';

const result = await Translation.present({ text: 'Hello' });
// result is now TranslationResult, replacing the old Promise<number> type.

The original named present export remains available from react-native-tinykit/translation for compatibility.

Add Translation to your app's feature list if configured, remove the old package, then run pod install and rebuild. TinyKit replaces the original private selector with the public API, preserves emoji, and completes the previously unresolved Promise. The minimum OS for this feature changes from iOS 15 to iOS 17.4. The migration is based on react-native-ios-translation 0.2.2; its MIT notice is retained in ios/Translation/LICENSE.

From @react-native-library/overlay

Change the import to react-native-tinykit and add the Podfile helper above. The Toast.show, Alert.show, Alert.dismissAll, Confetti.start and Confetti.stop APIs retain their names and millisecond duration units. The standalone dismissAll export is also retained. The old overlay README's Confetti.show example was incorrect; the actual API is Confetti.start.

Public option types are available as ToastOptions, AlertOptions, ConfettiOptions and OverlayHaptic.

Example App

The example app demonstrates native utilities, overlays, and Translation. From the repository root:

yarn

Install the example's Pods:

cd example/ios && pod install

Then run Metro and the iOS app from the repository root in separate terminals:

yarn example start
yarn example ios

Use a physical device to exercise Translation. See the development workflow for build and validation commands.

Apps Using This Library

Contributing

See the contributing guide to learn how to contribute to the repository and the development workflow.

License

MIT © Darkce


Made with create-react-native-library