High-performance React Native contacts library built with JSI and Nitro Modules. Access the device address book with minimal overhead and full TypeScript support.
- JSI (Hybrid Objects) — No bridge; synchronous where it matters, async for heavy work
- Universal support — Works with both New Architecture and Old Architecture (React Native 0.74+)
- Typed API — Full TypeScript types:
Contact,PostalAddress,LabeledValue,PermissionStatus - Single hook —
useContacts()handles permissions, loading, errors, and refresh - Expo-ready — Config plugin for managed workflow (permissions + autolinking)
- React Native 0.74+
- iOS 13+ / Android (API 21+)
Nitro Modules is included as a dependency — you do not need to install react-native-nitro-modules in your app. Everything is bundled with this package.
bun add react-native-nitro-contacts
# or
npm install react-native-nitro-contacts
# or
yarn add react-native-nitro-contactsiOS: After installing, run CocoaPods:
cd ios && pod install- Install the package and add the config plugin in
app.json/app.config.js:
{
"expo": {
"plugins": [
["react-native-nitro-contacts", { "contactsPermissionText": "Your custom permission message for iOS." }]
]
}
}- Run prebuild (if you use a dev build):
npx expo prebuild- iOS: Add
NSContactsUsageDescriptiontoInfo.plist. - Android: Add
READ_CONTACTStoAndroidManifest.xml(or use the Expo config plugin logic as reference).
Then run pod install in the ios folder.
import { useContacts, PermissionStatus, type Contact } from 'react-native-nitro-contacts'
function ContactsScreen() {
const { contacts, status, isLoading, error, requestPermission, refresh } = useContacts()
if (isLoading) return <Loading />
if (error) return <Error onRetry={refresh} message={error.message} />
if (status !== PermissionStatus.GRANTED) {
return <Button onPress={requestPermission} title="Grant access" />
}
return (
<FlatList
data={contacts}
keyExtractor={(c) => c.id}
renderItem={({ item }) => <ContactRow contact={item} />}
/>
)
}| Property | Type | Description |
|---|---|---|
contacts |
Contact[] |
List of contacts (empty until granted) |
status |
PermissionStatus |
GRANTED | DENIED | NOT_DETERMINED | LIMITED |
isLoading |
boolean |
Initial load or refresh in progress |
error |
Error | null |
Last error if any |
requestPermission |
() => Promise<boolean> |
Request permission (iOS/Android) |
refresh |
() => Promise<void> |
Reload contacts |
id,displayName,givenName,middleName,familyNamecompany,jobTitle,department,notebirthday?: stringemails,phoneNumbers,postalAddresses,urlAddresses(arrays of labeled values)thumbnailPath?,hasImage
See PostalAddress and LabeledValue in the package exports for nested types.
| Path | Description |
|---|---|
packages/react-native-nitro-contacts |
Library source (TypeScript, Nitro spec, native iOS/Android) |
apps/example |
Expo example app (SDK 50+) |
This repo uses Bun for scripts and workspaces.
# Install dependencies (root)
bun install
# Typecheck
bun run typecheck
# Build the library
bun run build:libcd apps/example
bun install # if not already done from root
bun run ios # or: bun run android- Runtime / package manager: Bun
- Native: Swift (iOS), Kotlin (Android) via Nitro Hybrid Objects
- Example app: Expo SDK 50+ with config plugins
MIT