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

@servable/react-native

v1.0.0

Published

Servable client for React Native - routes, auth, user and hooks

Readme

@servable/react-native

Servable client for React Native — routes, auth, current user, and hooks. The React Native counterpart to @servable/nextjs, with the same surface (Routes, User, hooks, store) and the same request pipeline: device params, W3C trace propagation, retry with backoff, access-token refresh, and step-up handling.

Install

yarn add @servable/react-native axios @react-native-async-storage/async-storage

axios, react, react-native and AsyncStorage are peer dependencies. AsyncStorage is optional if you inject your own storage adapter (see below).

Configure

There are no NEXT_PUBLIC_* environment variables here — Metro does not substitute env vars the way Next inlines them at build time — so everything is configured once at app start:

import Servable from '@servable/react-native'
import AsyncStorage from '@react-native-async-storage/async-storage'
import * as Keychain from 'react-native-keychain'

Servable.configure({
  serverUrl: 'https://backend.example.com',   // required
  version: 'v1',
  timeout: 15000,
  platformId: 'your-platform-id',

  storage: AsyncStorage,                       // optional; auto-detected if installed
  secureStorage: myKeychainAdapter,            // strongly recommended - see Security

  onAccountRedirect: () => navigation.navigate('SignIn'),
  onCurrentUserChanged: (user) => userStore.set(user),
  sentry: Sentry,                              // optional, @sentry/react-native
})

onAccountRedirect and onCurrentUserChanged replace the two host-app modules the web package imported through Next path aliases (lib/account/lib/openaccountredirect, lib/contexts/currentUserContext). A library can't reach into its consumer, so they're injected.

Use

const { result, error } = await Servable.Routes.Get({ path: 'account/me' })
await Servable.Routes.Post({ path: 'publication/create', params: { name: 'My pub' } })

// Uploads take React Native's file descriptor shape
await Servable.Routes.Function({
  name: 'uploadAvatar',
  files: [{ uri: asset.uri, fileName: 'avatar.jpg', type: 'image/jpeg' }],
})

const user = await Servable.User.currentAsync()
await Servable.User.signOut()

On app start (and on resume), re-mint the access token — it's held in memory only, so it's empty after every cold start:

useEffect(() => { Servable.refreshAccessToken() }, [])

Deep links and install referrers feed attribution, in place of the web's UTM query parameters:

Servable.setSourceParams({ sourceNature: 'referral', sourceId: 'abc' })

How this differs from @servable/nextjs

Most of the package is a direct port. These are the places where the platform forced a real change rather than a cosmetic one.

Storage is async. getStoreValue/setStoreValue and the Routes.Get response cache all return promises. Their web counterparts are synchronous because cookies and localStorage are; AsyncStorage is not. Any code moved over from the web package must await them.

Refresh tokens are stored by the app. On the web the refresh token is an httpOnly cookie — never readable by JS, resent automatically. React Native has no httpOnly cookies, so the token is stored and sent explicitly. That makes where it's stored a security decision, which is why secureStorage is a separate adapter: point it at Keychain / EncryptedSharedPreferences (react-native-keychain, expo-secure-store). It falls back to ordinary storage, which is not appropriate for production.

No SSR. The context argument (a Next { req, res } pair) is gone from every function.

No custom-domain proxy. The web package routed custom-domain traffic through a same-origin proxy and carried a _pk_sess cookie fallback, both purely to survive browser third-party cookie policy. Native has no origin and no cookie jar; requests always go straight to serverUrl.

An unset serverUrl throws. The web version fell back to a relative /v1/path, which is a valid same-origin URL in a browser. On native a relative URL is just a broken request, so this surfaces immediately as a configuration error.

Uploads use file descriptors, not Blobs. React Native's FormData takes { uri, name, type } and streams from disk; appending a Blob produces a malformed part rather than an error. Routes.Function accepts the native shape, a base64 data URI, or a json value, and normalizes all three.

No atob/btoa/TextEncoder. None exist in Hermes or JSC, so JWT decoding and base64 encoding use Buffer when present and a pure-JS path otherwise.

Sentry is optional. @sentry/nextjs can't be installed in an RN app, and @sentry/react-native shouldn't be forced on consumers who don't use it — pass it via configure({ sentry }) or set a global. Without it, trace propagation still works; requests just aren't joined to a Sentry transaction.

User.signOut() is new. On the web, signing out clears server-controlled cookies, so the client had nothing to do. Here every credential is app-held and must be explicitly discarded.

Tests

yarn test

ESM-native, run under --experimental-vm-modules with testEnvironment: "node" — deliberately not jsdom, so anything that only works because a browser global happens to exist fails loudly instead of passing in an environment the real runtime doesn't resemble. Use jest.unstable_mockModule with dynamic import(); plain jest.mock will not work.