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

@nonito/react-native-sdk

v0.1.0-beta.1

Published

Nonito In-App Messaging SDK for React Native

Readme

@nonito/react-native-sdk

In-app messaging SDK for React Native. Fetches message definitions from the Nonito backend, evaluates trigger rules on-device as users navigate, and renders modals, banners, fullscreen takeovers, and bottom sheets natively.

Install

npm install @nonito/react-native-sdk @react-native-async-storage/async-storage

@react-native-async-storage/async-storage is a required peer dependency — the SDK uses it to persist identity and frequency-cap state. On iOS, run cd ios && pod install.

Peer requirements: React >= 18, React Native >= 0.72.

Quick start

Wrap your app in NonitoProvider. It configures the SDK, subscribes to display events, and renders matched messages above your UI.

import { NonitoProvider } from '@nonito/react-native-sdk';

export default function App() {
  return (
    <NonitoProvider appKey="nk_live_abc123">
      <RootNavigator />
    </NonitoProvider>
  );
}

Then track screens and events. Each call re-evaluates trigger rules against the cached messages.

import { useNonito, useTrackScreen } from '@nonito/react-native-sdk';

function CheckoutScreen() {
  useTrackScreen('checkout'); // fires on mount

  const { identify, trackEvent } = useNonito();

  return (
    <Button
      title="Buy"
      onPress={() => trackEvent('purchase', { total: 249 })}
    />
  );
}

Identify customers after login so attribute-based triggers can match:

const { identify, reset } = useNonito();

await identify('customer_123', { tier: 'gold', total_orders: 5 });
await reset(); // on logout

Configuration

<NonitoProvider
  appKey="nk_live_abc123"
  apiBaseUrl="https://api.nonito.xyz"
  config={{
    logLevel: 'debug',
    inAppMessagesEnabled: true,
    fetchIntervalMs: 300_000,
    flushIntervalMs: 30_000,
  }}
>

| Option | Default | Purpose | |---|---|---| | appKey | — | Required. From the Nonito dashboard. | | apiBaseUrl | https://api.nonito.xyz | Nonito API base URL. The analytics server owns the /sdk/* endpoints. | | logLevel | warn | none | error | warn | info | debug | | inAppMessagesEnabled | true | Master switch for in-app messaging. | | pushEnabled | true | Reserved — push is not yet implemented. | | fetchIntervalMs | 300000 | Message definition poll interval. | | flushIntervalMs | 30000 | Analytics event flush interval. |

How triggering works

On configure, the SDK registers the device, downloads eligible message definitions, and caches them locally. It re-polls on fetchIntervalMs and refetches whenever identity changes.

Every trackScreen() / trackEvent() call runs the trigger engine over the cached set. Rules are OR'd groups of AND'd conditions:

[
  {
    "id": "g1",
    "conditions": [
      { "type": "screen",    "operator": "equals", "value": "checkout" },
      { "type": "attribute", "attribute": "total_orders", "operator": "gte", "value": 3 }
    ]
  }
]

Condition types: event, screen, attribute, session. Operators: equals, not_equals, contains, gte, lte, in.

Messages that pass frequency capping are sorted by priority descending, and the first match wins — only one message displays at a time.

equals and not_equals fall back to string comparison, so 5 matches "5". Attribute paths are dot-notated (profile.tier).

Frequency capping

Each message carries a frequency block, enforced on-device and persisted across launches:

| Field | Meaning | |---|---| | max_impressions_per_device | Lifetime cap. 0 disables. | | max_per_session | Per-session cap. Session counters reset on launch. | | cooldown_hours | Minimum hours between impressions. 0 disables. |

Handling CTAs

NonitoProvider reports CTA clicks and dismisses automatically. To act on them — deep links, navigation — register a handler:

const { sdk } = useNonito();

sdk?.setActionHandler((action) => {
  if (action.actionType === 'deep_link' && action.actionValue) {
    Linking.openURL(action.actionValue);
  }
});

Suppress a message conditionally by returning false:

sdk?.setWillDisplayHandler((message) => !isInCriticalFlow);

Imperative API

If you'd rather not use the provider:

import { Nonito } from '@nonito/react-native-sdk';

await Nonito.configure({ appKey: 'nk_live_abc123' });

Nonito.shared.trackScreen('checkout');
Nonito.shared.trackEvent('add_to_cart', { sku: 'ABC' });
await Nonito.shared.identify('customer_123', { tier: 'gold' });
await Nonito.shared.shutdown();

Nonito.configure() is idempotent — calling it twice warns and returns the existing instance. Nonito.shared throws if accessed before configure.

Testing without a backend

const { sdk } = useNonito();
sdk?.inApp.setTestMessages([myMessageFixture]);

Backend endpoints

The SDK expects three endpoints on apiBaseUrl:

| Endpoint | Purpose | |---|---| | POST /sdk/register | Register device token + customer identity | | GET /sdk/in-app-messages | Fetch eligible messages and trigger rules | | POST /sdk/events | Batch analytics events (impressions, clicks, dismisses) |

Development

npm install
npm test          # 173 tests
npm run typecheck
npm run build

Trigger-engine behavior is pinned by shared JSON fixtures in ../fixtures/, run against both this SDK and the Flutter SDK to keep the two implementations in lockstep. See eval/ at the repo root.

License

MIT