This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.
Expo Contacts
A library that provides access to the phone's system contacts.
expo-contacts provides access to the device's system contacts, allowing you to get contact information as well as adding, editing, or removing contacts.
On iOS, contacts have a multi-layered grouping system that you can also access through this API.
Installation
If you are installing this in an existing React Native app, make sure to install expo in your project.
Configuration in app config
You can configure expo-contacts using its built-in config plugin if you use config plugins in your project (Continuous Native Generation (CNG)). The plugin allows you to configure various properties that cannot be set at runtime and require building a new app binary to take effect. If your app does not use CNG, then you'll need to manually configure the library.
Example app.json with config plugin
Configurable properties
Are you using this library in an existing React Native app?
If you're not using Continuous Native Generation (CNG) (you're using native android and ios projects manually), then you need to configure following permissions in your native projects:
-
For Android, add
android.permission.READ_CONTACTSandandroid.permission.WRITE_CONTACTSpermissions to your project's android/app/src/main/AndroidManifest.xml:<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /> -
For iOS, add the
NSContactsUsageDescriptionkey to your project's ios/[app]/Info.plist:<key>NSContactsUsageDescription</key> <string>Allow $(PRODUCT_NAME) to access your contacts</string>
Usage
API
import * as Contacts from 'expo-contacts';
Component
Type: React.PureComponent<ContactAccessButtonProps>
Creates a contact access button to quickly add contacts under limited-access authorization.
For more details, you can read the Apple docs about the underlying ContactAccessButton SwiftUI view.
ColorValueA color of the button's background. Provided color should not be transparent, otherwise it may not satisfy platform requirements for button legibility.
stringWhen the query produces a single result, the contact access button shows the caption under the matching contact name. It can be nothing (default), email address or phone number.
Acceptable values are: 'default' | 'email' | 'phone'
string[]An array of email addresses. The search omits contacts matching query that also match any email address in this array.
string[]An array of phone numbers. The search omits contacts matching query that also match any phone number in this set.
stringA string to match against contacts not yet exposed to the app. You typically get this value from a search UI that your app presents, like a text field.
ColorValueA color of the button's title. Slightly dimmed version of this color is used for the caption text. Make sure there is a good contrast between the text and the background, otherwise platform requirements for button legibility may not be satisfied.
ColorValueA tint color of the button and the modal that is presented when there is more than one match.
Static methods
Returns a boolean whether the ContactAccessButton is available on the platform.
This is true only on iOS 18.0 and newer.
booleanConstants
Methods
Creates a new contact and adds it to the system.
Note: For Android users, the Expo Go app does not have the required
WRITE_CONTACTSpermission to write to Contacts. You will need to create a development build and add permission in there manually to use this method.
Promise<string>A promise that fulfills with ID of the new system contact.
Example
const contact = { [Contacts.Fields.FirstName]: 'Bird', [Contacts.Fields.LastName]: 'Man', [Contacts.Fields.Company]: 'Young Money', }; const contactId = await Contacts.addContactAsync(contact);
Add a contact as a member to a group. A contact can be a member of multiple groups.
Promise<any>Example
await Contacts.addExistingContactToGroupAsync( '665FDBCFAE55-D614-4A15-8DC6-161A368D', '161A368D-D614-4A15-8DC6-665FDBCFAE55' );
Add a group to a container.
Promise<any>Example
await Contacts.addExistingGroupToContainerAsync( '161A368D-D614-4A15-8DC6-665FDBCFAE55', '665FDBCFAE55-D614-4A15-8DC6-161A368D' );
Create a group with a name, and add it to a container. If the container is undefined, the default container will be targeted.
Promise<string>A promise that fulfills with ID of the new group.
Example
const groupId = await Contacts.createGroupAsync('Sailor Moon');
Used for gathering precise data about a contact. Returns a contact matching the given id.
Promise<ExistingContact | undefined>A promise that fulfills with Contact object with ID matching the input ID, or undefined if there is no match.
Example
const contact = await Contacts.getContactByIdAsync('161A368D-D614-4A15-8DC6-665FDBCFAE55'); if (contact) { console.log(contact); }
Return a list of contacts that fit a given criteria. You can get all of the contacts by passing no criteria.
Promise<ContactResponse>A promise that fulfills with ContactResponse object returned from the query.
Example
const { data } = await Contacts.getContactsAsync({ fields: [Contacts.Fields.Emails], }); if (data.length > 0) { const contact = data[0]; console.log(contact); }
Query a list of system containers.
Promise<Container[]>A promise that fulfills with array of containers that fit the query.
Example
const allContainers = await Contacts.getContainersAsync({ contactId: '665FDBCFAE55-D614-4A15-8DC6-161A368D', });
Get the default container's ID.
Promise<string>A promise that fulfills with default container ID.
Example
const containerId = await Contacts.getDefaultContainerIdAsync();
Checks user's permissions for accessing contacts data.
Promise<ContactsPermissionResponse>A promise that resolves to a ContactsPermissionResponse object.
Checks if any contacts exist on the device without querying all contacts. This method requires contacts read permission.
Promise<boolean>A promise that fulfills with a boolean, indicating whether there are any contacts on the device.
Example
const hasContacts = await Contacts.hasContactsAsync(); if (hasContacts) { console.log('Contacts are available'); }
Returns whether the Contacts API is enabled on the current device. This method does not check the app permissions.
Promise<boolean>A promise that fulfills with a boolean, indicating whether the Contacts API is available on the current device. It always resolves to false on web.
Presents a modal which allows the user to select which contacts the app has access to. Using this function is reasonable only when the app has "limited" permissions.
Promise<string[]>A promise that resolves with an array of contact identifiers that were newly granted to the app. Contacts which the app lost access to are not listed. On platforms other than iOS and below 18.0, the promise rejects immediately.
Presents a native contact picker to select a single contact from the system. On Android, the READ_CONTACTS permission is required. You can
obtain this permission by calling the Contacts.requestPermissionsAsync() method. On iOS, no permissions are
required to use this method.
Promise<ExistingContact | null>A promise that fulfills with a single Contact object if a contact is selected or null if no contact is selected (when selection is canceled).
Present a native form for manipulating contacts.
Promise<any>Example
await Contacts.presentFormAsync('161A368D-D614-4A15-8DC6-665FDBCFAE55');
Delete a contact from the system.
Promise<any>Example
await Contacts.removeContactAsync('161A368D-D614-4A15-8DC6-665FDBCFAE55');
Remove a contact's membership from a given group. This will not delete the contact.
Promise<any>Example
await Contacts.removeContactFromGroupAsync( '665FDBCFAE55-D614-4A15-8DC6-161A368D', '161A368D-D614-4A15-8DC6-665FDBCFAE55' );
Delete a group from the device.
Promise<any>Example
await Contacts.removeGroupAsync('161A368D-D614-4A15-8DC6-665FDBCFAE55');
Asks the user to grant permissions for accessing contacts data.
Promise<ContactsPermissionResponse>A promise that resolves to a ContactsPermissionResponse object.
Mutate the information of an existing contact. Due to an iOS bug, nonGregorianBirthday field cannot be modified.
Promise<string>A promise that fulfills with ID of the updated system contact if mutation was successful.
Example
const contact = { id: '161A368D-D614-4A15-8DC6-665FDBCFAE55', [Contacts.Fields.FirstName]: 'Drake', [Contacts.Fields.Company]: 'Young Money', }; await Contacts.updateContactAsync(contact);
Change the name of an existing group.
Promise<any>Example
await Contacts.updateGroupName('Expo Friends', '161A368D-D614-4A15-8DC6-665FDBCFAE55');
Query a set of contacts and write them to a local URI that can be used for sharing.
Promise<string | undefined>A promise that fulfills with shareable local URI, or undefined if there was no match.
Example
const localUri = await Contacts.writeContactToFileAsync({ id: '161A368D-D614-4A15-8DC6-665FDBCFAE55', }); Share.share({ url: localUri, message: 'Call me!' });
Event subscriptions
Adds a listener for contact changes. The listener will be called whenever contacts are added, updated, or deleted.
Platform differences:
- Android: 5-7 second delay - uses
ContentObserverwith inherent system delays - iOS: Immediate response - uses
CNContactStoreDidChangeNotification
The Android delay is a system limitation that affects all apps using ContentObserver for contacts.
This delay is by design to batch notifications for better performance and battery life.
For more immediate updates, you can also listen to app state changes and refresh
contacts when the app comes to the foreground. This ensures users see the latest contacts when
returning from the native Contacts app.
EventSubscriptionA subscription object with a remove method to stop listening.
Example
const subscription = Contacts.addContactChangeListener(() => { console.log('Contacts changed - refreshing contact list'); // Refresh your contact list when changes are detected loadContacts(); }); // Later, remove the listener subscription.remove();
Types
Literal type: union
Acceptable values are: CalendarFormats | {CalendarFormats}
Base contact type without ID. For better type safety, consider using:
Contactwhen creating new contacts (no ID needed)ExistingContactwhen working with contacts returned from the system (ID guaranteed)
String union of SortTypes values.
Type for existing contacts returned from the system - guarantees the id field is present.
Type: Contact extended by:
A parent to contacts. A contact can belong to multiple groups. Here are some query operations you can perform:
- Child Contacts:
getContactsAsync({ groupId }) - Groups From Container:
getGroupsAsync({ containerId }) - Groups Named:
getContainersAsync({ groupName })
Information regarding thumbnail images.
On Android you can get dimensions using
Image.getSizemethod.
Literal type: union
Permission expiration time. Currently, all permissions are granted permanently.
Acceptable values are: 'never' | number
Enums
This format denotes the common calendar format used to specify how a date is calculated in nonGregorianBirthday fields.
Possible fields to retrieve for a contact.
Permissions
Android
This library automatically adds READ_CONTACTS and WRITE_CONTACTS permissions to your app:
iOS
The following usage description keys are used by this library: