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

expo-block-store

v1.0.0

Published

Expo module wrapping the Android Block Store API (Google Play Services) — persist small tokens or identifiers that survive app uninstall/reinstall and transfer to a new device, with no sign-in. iOS and web are safe no-ops.

Readme

expo-block-store

npm version license

An Expo module that wraps the Android Block Store API (Google Play services). Store small tokens or identifiers that survive app uninstall/reinstall and transfer to a new device during setup — with no sign-in screen.

Built with the Expo Modules API, so it works with both the New Architecture and the legacy bridge. iOS and web are safe no-ops (no native code, calls resolve to neutral defaults), so the same call sites compile and run everywhere without Platform branching.

Why

Apps without traditional auth often use a client-generated id as the user's identity. On iOS that id can be recovered from StoreKit transaction history or the Keychain (both survive uninstall). On Android there is no equivalent: BillingClient only returns active purchases, and Keystore/SharedPreferences are wiped on uninstall. Block Store is Google's purpose-built primitive for exactly this — a tiny, app-private, optionally end-to-end-encrypted blob that rides the Android Backup infrastructure.

| Source | Survives same-device reinstall | Survives new device | | --- | --- | --- | | SharedPreferences / MMKV / expo-secure-store (Android) | ❌ | ❌ | | Play Billing obfuscatedAccountId | only while a purchase is active | only with an active purchase | | Block Store | ✅ (Backup enabled) | ✅ (cloud / device-to-device restore) |

Installation

npx expo install expo-block-store

Then rebuild the native project (Continuous Native Generation):

npx expo prebuild --clean

It pulls in com.google.android.gms:play-services-auth-blockstore. No manifest changes or extra setup required.

Quick start

import * as BlockStore from 'expo-block-store';

// Persist an anonymous user id so it survives reinstall.
await BlockStore.setItem('com.myapp.userId', userId, { shouldBackupToCloud: true });

// On next launch / after reinstall — recover it.
const recovered = await BlockStore.getItem('com.myapp.userId');
// → "550E8400-E29B-41D4-A716-446655440000" | null

API

All functions are async. On iOS/web they resolve to the neutral values noted below and never throw. On Android the read/write promises may reject (size limit, entry-count limit, Play services unavailable) — handle that at the call site.

setItem(key, value, options?) => Promise<boolean>

Stores value (a string, ≤ MAX_ENTRY_SIZE bytes UTF-8) under key. Resolves true on success. iOS/web: false.

await BlockStore.setItem('com.myapp.userId', userId, { shouldBackupToCloud: true });

options.shouldBackupToCloud (default false): also back the entry up to the cloud so it can be restored on a new device. Requires the user's Google Backup to be enabled and is end-to-end encrypted when the device supports it.

⚠️ Cloud gotcha: if you later call setItem for the same data without shouldBackupToCloud: true, the previously cloud-backed bytes are deleted from the cloud on the next sync. Set the flag consistently.

getItem(key) => Promise<string | null>

Reads the string stored under key, or null if absent. iOS/web: null.

getAllItems() => Promise<Record<string, string>>

Reads every entry this app stored as a key → value map. iOS/web: {}.

removeItem(key) => Promise<boolean>

Deletes the entry under key. Resolves true if something was deleted. iOS/web: false.

clear() => Promise<boolean>

Deletes every entry this app stored. iOS/web: false.

isEndToEndEncryptionAvailable() => Promise<boolean>

Whether data stored right now will be end-to-end encrypted (Android 9+ with a device screen lock). Use it to decide whether to enable cloud backup. iOS/web: false.

const e2ee = await BlockStore.isEndToEndEncryptionAvailable();
await BlockStore.setItem(key, value, { shouldBackupToCloud: e2ee });

isSupported() => boolean

true only on Android with the native module linked. Use it to branch recovery logic.

Constants

  • MAX_ENTRY_SIZE4096 (bytes per entry)
  • MAX_ENTRY_COUNT16 (entries per app)

How it works

Block Store keeps app-private blobs on top of the Android Backup system. Data is readable only by the same app (verified via package name + signing signature).

  • Same device, uninstall → reinstall: restored if the user has Backup enabled (Settings → Google → Backup).
  • New device: transferred via device-to-device restore or cloud restore during setup (requires shouldBackupToCloud: true).

| Capability | Minimum | | --- | --- | | Block Store (store/retrieve/delete) | Android 6 (API 23) + Google Play services | | End-to-end encryption | Android 9 (API 29) + screen lock | | Cloud restore (new device) | Pixel: Android 9 (API 29) · others: Android 12 (API 31) |

Caveats

  • Backup toggle: persistence depends on the user's Google Backup being on. It's on by default for most signed-in users, but not guaranteed — treat Block Store as one recovery layer, not the sole source of truth.
  • Capacity: up to 16 entries × 4 KB. Plenty for ids/tokens, not for documents.
  • Not for secrets you can't rotate. It's a recoverable identifier store; pair sensitive tokens with server-side validation.

Platform behavior

| Platform | Backend | | --- | --- | | Android | Google Play services Block Store | | iOS | no-op (resolves null/false/{}) | | Web | no-op (resolves null/false/{}) |

On iOS, recovering an anonymous id is typically already covered by StoreKit transaction history or the Keychain, so this module focuses on closing the Android gap.

License

MIT © Dmitry Matatov