@capacitor/contacts
Access, search, pick, create, update and remove device contacts.
Installโ
To use npm
npm install @capacitor/contacts
To use yarn
yarn add @capacitor/contacts
Sync native files
npx cap sync
iOSโ
Add the NSContactsUsageDescription key to your app's Info.plist and
describe why the app accesses the user's contacts; iOS crashes on first
contacts access without it:
<key>NSContactsUsageDescription</key>
<string>We need access to contacts to search, save and remove them.</string>
Notes:
- The plugin uses the modern Contacts framework (
CNContactStore, never the deprecated AddressBook APIs), so it fully supports iOS 18+ Limited Access:find,saveandremoveoperate on the subset the user shared with the app, andpickContactpresents the system picker (which needs no permission and always shows the full contact list). - The
notefield is not supported on iOS: reading or writing it requires Apple's restrictedcom.apple.developer.contacts.notesentitlement. The field is omitted on read and ignored on save. - Contact
ids are opaqueCNContactidentifiers; ids persisted by the legacy AddressBook-based Cordova plugin do not resolve after migration.
Androidโ
The plugin declares READ_CONTACTS and WRITE_CONTACTS in its own
manifest; Gradle manifest merging adds them to your app automatically. Each
method requests the runtime permission it needs the first time it runs;
read for find/pickContact, read + write for save/remove.
Permission modelโ
This plugin intentionally exposes no checkPermissions() /
requestPermissions() methods: permissions are requested implicitly by each
method, matching the legacy cordova-plugin-contacts behavior. A call
rejects with OS-PLUG-CONT-0020 when the user denies access.
Errorsโ
Every rejection carries a structured code + message:
| Code | Meaning |
|---|---|
OS-PLUG-CONT-0000 | Unknown error |
OS-PLUG-CONT-0001 | Invalid argument (e.g. unknown id) |
OS-PLUG-CONT-0002 | Timeout (reserved, currently unused) |
OS-PLUG-CONT-0003 | Pending operation (e.g. picker already open) |
OS-PLUG-CONT-0004 | I/O error |
OS-PLUG-CONT-0005 | Not supported |
OS-PLUG-CONT-0006 | Operation cancelled (picker closed) |
OS-PLUG-CONT-0020 | Permission denied |
Usageโ
import { Contacts } from '@capacitor/contacts';
// Search every field for "ada" and return all matches with a phone number
const { contacts } = await Contacts.find({
fields: ['*'],
filter: 'ada',
multiple: true,
hasPhoneNumber: true,
});
// Create a contact
const saved = await Contacts.save({
contact: {
name: { givenName: 'Ada', familyName: 'Lovelace' },
phoneNumbers: [{ type: 'mobile', value: '+351910000000' }],
emails: [{ type: 'home', value: '[email protected]' }],
},
});
// Update it (id present -> update)
await Contacts.save({ contact: { ...saved, nickname: 'Countess' } });
// Pick a contact with the native picker
const picked = await Contacts.pickContact();
// Remove it by id
await Contacts.remove({ id: saved.id! });
APIโ
find(...)โ
find(options: ContactFindOptions) => Promise<ContactFindResult>
Queries the device contacts database and returns the matching contacts.
Requests the READ_CONTACTS (Android) / Contacts (iOS) permission internally the first time it runs; there is no separate permission method.
iOS 18+: under Limited Access the search runs against (and returns only) the subset of contacts the user shared with the app.
| Param | Type |
|---|---|
options | |
Returns:
Promise<ContactFindResult>
Since: 1.0.0
save(...)โ
save(options: ContactSaveOptions) => Promise<Contact>
Persists a new contact or updates an existing one (matched by contact.id).
Resolves with the full saved contact.
Requests the READ/WRITE_CONTACTS (Android) / Contacts (iOS) permission internally.
iOS 18+: works under Limited Access: new contacts are added to the
app's accessible set; updating requires the target contact to be in that
set (otherwise the call rejects with OS-PLUG-CONT-0001).
| Param | Type |
|---|---|
options | |
Returns:
Promise<Contact>
Since: 1.0.0
remove(...)โ
remove(options: ContactRemoveOptions) => Promise<void>
Removes the contact with the given id from the device contacts database.
Rejects with OS-PLUG-CONT-0001 when no contact has that id (on iOS 18+
Limited Access, also when the contact is outside the accessible set).
Requests the READ/WRITE_CONTACTS (Android) / Contacts (iOS) permission internally.
| Param | Type |
|---|---|
options | |
Since: 1.0.0
pickContact()โ
pickContact() => Promise<Contact>
Launches the native contact picker UI and resolves with the contact the
user selects. Rejects with OS-PLUG-CONT-0006 if the user cancels.
On iOS the system picker requires no permission and always shows the full contact list, even under iOS 18+ Limited Access (the picked contact is returned without joining the app's accessible set). On Android the READ_CONTACTS permission is requested internally to read the picked contact's details.
Returns:
Promise<Contact>
Since: 1.0.0
Interfacesโ
ContactFindResultโ
Result of a {@link ContactsPlugin.find} call.
| Prop | Type | Description | Since |
|---|---|---|---|
contacts | Contact[] | The contacts matching the search criteria. | 1.0.0 |
Contactโ
A single device contact.
| Prop | Type | Description | Since |
|---|---|---|---|
id | string | Globally unique, platform-assigned identifier. Absent for contacts not yet saved to the device. | 1.0.0 |
rawId | string | Android raw-contact id backing this aggregated contact. iOS leaves this unset. | 1.0.0 |
displayName | string | Name suitable for display to end users. | 1.0.0 |
name | | The structured name components. | 1.0.0 |
nickname | string | A casual name by which to address the contact. | 1.0.0 |
phoneNumbers | ContactField[] | The contact's phone numbers. | 1.0.0 |
emails | ContactField[] | The contact's email addresses. | 1.0.0 |
addresses | ContactAddress[] | The contact's postal addresses. | 1.0.0 |
ims | ContactField[] | The contact's instant-messaging handles. | 1.0.0 |
organizations | ContactOrganization[] | The contact's organizations. | 1.0.0 |
birthday | number | The contact's birthday as epoch milliseconds. | 1.0.0 |
note | string | A free-form note about the contact. iOS: not supported by default: reading/writing a contact's note requires Apple's restricted com.apple.developer.contacts.notes entitlement. Without it the field is omitted on read and ignored on save. Android has no such restriction. | 1.0.0 |
photos | ContactField[] | The contact's photos. Reads return type: 'url' with the value holding a reference to the image, never image bytes: on Android the contact's content:// photo URI, on iOS the path of a copy written to the app's temporary directory (cleared by the system). On save, the first entry is applied: pass type: 'base64' with base64 data, or type: 'url' with a local file:///content:// URI to import. | 1.0.0 |
categories | ContactField[] | User-defined categories associated with the contact. Read-only: populated from the contact's group memberships on Android, never returned on iOS (the Contacts framework has no equivalent), and ignored on save. | 1.0.0 |
urls | ContactField[] | Web pages associated with the contact. | 1.0.0 |
ContactNameโ
Structured name of a {@link Contact}.
| Prop | Type | Description | Since |
|---|---|---|---|
formatted | string | The complete formatted name. | 1.0.0 |
familyName | string | Family (last) name. | 1.0.0 |
givenName | string | Given (first) name. | 1.0.0 |
middleName | string | Middle name. | 1.0.0 |
honorificPrefix | string | Honorific prefix (e.g. Mr., Dr.). | 1.0.0 |
honorificSuffix | string | Honorific suffix (e.g. Esq.). | 1.0.0 |
ContactFieldโ
A generic, repeatable contact field (phone number, email, IM, photo, URL, category).
| Prop | Type | Description | Since |
|---|---|---|---|
type | string | The kind of field, e.g. home, work, mobile. For photos, url or base64. | 1.0.0 |
value | string | The field value (phone number, email address, URI, etc.). | 1.0.0 |
pref | boolean | true if this is the contact's preferred value for the field. | 1.0.0 |
id | string | Platform-assigned id of this individual field entry. | 1.0.0 |
ContactAddressโ
A postal address of a {@link Contact}.
| Prop | Type | Description | Since |
|---|---|---|---|
id | string | Platform-assigned id of this address entry. | 1.0.0 |
pref | boolean | true if this is the contact's preferred address. | 1.0.0 |
type | string | The kind of address, e.g. home, work. | 1.0.0 |
formatted | string | The full address formatted for display. | 1.0.0 |
streetAddress | string | The street address. | 1.0.0 |
locality | string | The city or locality. | 1.0.0 |
region | string | The state or region. | 1.0.0 |
postalCode | string | The ZIP or postal code. | 1.0.0 |
country | string | The country name. | 1.0.0 |
ContactOrganizationโ
An organization a {@link Contact} belongs to.
| Prop | Type | Description | Since |
|---|---|---|---|
id | string | Platform-assigned id of this organization entry. Android only; iOS models the organization as flat contact properties without an id. | 1.0.0 |
pref | boolean | true if this is the contact's preferred organization. | 1.0.0 |
type | string | The kind of organization, e.g. work. | 1.0.0 |
name | string | The organization name. | 1.0.0 |
department | string | The department within the organization. | 1.0.0 |
title | string | The contact's title at the organization. | 1.0.0 |
ContactFindOptionsโ
Search options accepted by {@link ContactsPlugin.find}.
| Prop | Type | Description | Since |
|---|---|---|---|
fields | ContactFieldType[] | Fields to search against. Pass ['*'] to match every field. An empty array is invalid and rejects with OS-PLUG-CONT-0001. The id field matches by exact identifier; all other fields match case-insensitive substrings. photos and categories are not searchable. | 1.0.0 |
filter | string | Search string matched (case-insensitively) against the selected fields. An empty/omitted filter returns every contact. | 1.0.0 |
multiple | boolean | When true, returns every match; when false (default), returns at most one contact. | 1.0.0 |
desiredFields | ContactFieldType[] | If set, each returned {@link Contact} only includes these fields (plus the always-present id). | 1.0.0 |
hasPhoneNumber | boolean | OutSystems extension: when true, only contacts that have at least one phone number are returned. Defaults to false. | 1.0.0 |
ContactSaveOptionsโ
Options accepted by {@link ContactsPlugin.save}.
| Prop | Type | Description | Since |
|---|---|---|---|
contact | | The contact to create (no id) or update (existing id). Update semantics: every field present on the contact replaces the stored value entirely (e.g. name replaces the whole structured name, phoneNumbers replaces all phone numbers); omitted fields are left unchanged. | 1.0.0 |
ContactRemoveOptionsโ
Options accepted by {@link ContactsPlugin.remove}.
| Prop | Type | Description | Since |
|---|---|---|---|
id | string | The native id of the contact to remove. | 1.0.0 |
Type Aliasesโ
ContactFieldTypeโ
The set of contact fields a {@link ContactsPlugin.find} call can search
against or request back. Mirrors the legacy Cordova ContactFieldType
string values exactly.
'addresses' | 'birthday' | 'categories' | 'country' | 'department' | 'displayName' | 'emails' | 'familyName' | 'formatted' | 'givenName' | 'honorificPrefix' | 'honorificSuffix' | 'id' | 'ims' | 'locality' | 'middleName' | 'name' | 'nickname' | 'note' | 'organizations' | 'phoneNumbers' | 'photos' | 'postalCode' | 'region' | 'streetAddress' | 'title' | 'urls'