@notibase/react-native
v0.5.0
Published
Notibase for React Native and Expo — push notifications, an in-app inbox and attribution, with no native modules of its own
Maintainers
Readme
Pure TypeScript. Nothing to link, no pods, no Gradle, and no config plugin — so it works in Expo Go, in a development build, and in bare React Native without a prebuild. You keep the push library you already have and hand us the token.
Coming from OneSignal, or straight from Firebase
Notibase is an alternative to OneSignal, and the device-side model is the same shape: register a token, identify the person behind it, tag them, send to a segment. Most of a port is renaming calls. What is arranged differently is that push, an in-app inbox, email and SMS are one audience and one API here rather than several products with separate lists.
It is not an alternative to Firebase Cloud Messaging and does not try to be. You keep FCM and hand us the token: FCM delivers, Notibase decides who to deliver to — then carries the same campaign to iOS, the web, an inbox, an email and a text without you writing any of it a second time.
Install
npm install @notibase/react-native
# Recommended — without it, the device is only remembered until the app closes:
npx expo install @react-native-async-storage/async-storageQuick start
import { Notibase } from "@notibase/react-native";
await Notibase.configure("ck_live_…"); // publishable by designThen hand it your push token. Which call you use matters, and this is the one thing worth reading twice:
// Expo — the DEVICE token, not the Expo push token.
import * as Notifications from "expo-notifications";
const { data } = await Notifications.getDevicePushTokenAsync();
await Notibase.registerPushToken(data);
// @react-native-firebase/messaging — note the source.
import messaging from "@react-native-firebase/messaging";
const token = await messaging().getToken();
await Notibase.registerPushToken(token, { source: "firebase" });getExpoPushTokenAsync() returns a token only Expo's own push service can
deliver to, so registering one produces a device that can never receive
anything. The SDK refuses it by name rather than letting it fail silently.
On iOS, @react-native-firebase gives you an FCM token rather than an
APNs one, and they are not interchangeable — an FCM token sent to APNs is a
BadDeviceToken on every message, forever. You do not have to say which you
have: an Apple device token is 64 hexadecimal characters and a Firebase one is
several times longer with a colon in it, so the SDK reads the token and routes
it to the service that issued it. The source option is accepted and ignored;
it asked the app a question with a silent wrong answer.
Prefer messaging().getAPNSToken() where you can. It returns Apple's own
token, which Notibase delivers to directly with the .p8 you uploaded to
Notibase — getToken() returns Google's, which means Firebase relays and
needs its own copy of the key.
The rest of it
// Who this is. Everything they own becomes one audience member.
await Notibase.identify("user-42", { attributes: { plan: "pro" } });
// Events. `purchase` feeds attribution revenue.
await Notibase.track("viewed_cart", { items: 3 });
await Notibase.trackPurchase(499, "USD");
// Attribution: hand it the URL that opened the app.
await Notibase.handleDeepLink(url);
// Click tracking: hand it the notification's data payload.
await Notibase.trackNotificationOpen(remoteMessage.data);
// The in-app inbox.
const { items, unread } = (await Notibase.inbox()) ?? { items: [], unread: 0 };
await Notibase.inboxMarkRead(items.map((i) => i.id));
// Is any of this actually wired up? Run once from a debug build.
if (__DEV__) console.table(await Notibase.runSetupTest());In-app messages
A message shown inside your app rather than sent to it. You publish a rule in the console; the SDK caches it and this device decides when to show it — on app open, on foreground, or when you put a value in front of it.
import { Notibase, NotibaseInAppMessages } from "@notibase/react-native";
await Notibase.enableInAppMessages({
// Pressing an "Ask for push permission" button calls this. The SDK cannot
// ask by itself — permission belongs to the library you already have.
onPromptPush: () => Notifications.requestPermissionsAsync(),
// Optional: keep a campaign link inside the app instead of the browser.
onOpenUrl: (url) => navigation.navigate(url),
});
export default function App() {
return (
<NavigationContainer>
<Routes />
<NotibaseInAppMessages />
</NavigationContainer>
);
}Mount <NotibaseInAppMessages /> once, near the root. This package owns no
native module and no part of your view tree, so a message is a component you
place rather than something conjured over the top of your app — which is also
why your theme, your back button and your accessibility settings apply to it.
If you enable in-app messages and never mount it, the SDK says so in the
console rather than quietly showing nothing.
The component is normally at the root of your tree, so it is mounted while your splash or login screen is still what the person is looking at — and a message shows on top of that. Only your app knows when it has finished starting up:
Notibase.pauseInAppMessages(); // before configure()
Notibase.resumeInAppMessages(); // in the screen that replaces your splash
Notibase.inAppMessagesPaused; // true while messages are being heldNothing is lost while paused: rules are still fetched and the message stays eligible. Nothing is counted either — an impression is recorded when a message is drawn, so a held message has not spent its frequency cap and reports no display nobody saw. The pause is per process, so an app that pauses on every launch has to resume on every launch.
// Fires a campaign configured for "cart_value over 100", now. Local to the
// device: a trigger is a fact, not an event to report.
Notibase.setTrigger("cart_value", 240);// Android store installs: the only thing that can carry a campaign click
// through Google Play. Add react-native-play-install-referrer and pass it on.
await Notibase.setInstallReferrer(info.installReferrer);
await Notibase.logout(); // sign-out: the device stops being theirs
await Notibase.unsubscribe(); // "turn off notifications": a durable opt-outSigning out is not opting out. unsubscribe() records a durable opt-out
against the push token — it survives sign-in, reinstall and the
re-registration that happens on every launch, and is lifted only from your
own backend with a server key. Reaching for it on a sign-out button silences
the device for whoever signs in next, permanently. See
Signing somebody out.
A message is a document of text, image, button and spacer blocks, drawn with
ordinary React Native components. There is no WebView and no
dangerouslySetInnerHTML anywhere in it, which is the whole reason the
content is blocks rather than HTML somebody typed into a console.
The push-permission prime is the reason to reach for this first: both platforms give an app one system prompt per install and a decline is close to permanent, so it is worth spending only on somebody who already said yes inside your own UI.
What it does not do
- No notification permission prompt and no notification display — those belong
to
expo-notificationsor@react-native-firebase/messaging, which you already have. This SDK is the audience, targeting and reporting half.
Failures never reach your app
Every method swallows its own network errors and warns to the console. A notification SDK must not be the reason an app crashes, and an unhandled rejection in a lifecycle hook is a crash on both platforms.
Two things do throw, both before any network call and both because they fail
silently forever otherwise: a server key (sk_…) — which must never be in
an app bundle, because a bundle is public — and an Expo push token.
Storage
The device id has to survive a restart. @react-native-async-storage/async-storage
is used if it is installed and is an optional peer dependency, not a hard one.
Without it the SDK still works for the length of one launch and says so
loudly: every cold start would otherwise register a new device and inflate
your audience.
Any adapter with getItem/setItem/removeItem returning promises will do —
expo-secure-store, MMKV, your own:
await Notibase.configure("ck_live_…", { storage: myAdapter });Documentation
notibase.dev/react-native.html · Moving from OneSignal · REST API
