Skip to content

Getting Started with Contacts

This guide will walk you through integrating the Capacitor Contacts plugin into your application.

Installation

Install the plugin using npm:

Terminal window
npm install @capgo/capacitor-contacts
npx cap sync

iOS Configuration

Add the following to your Info.plist:

<key>NSContactsUsageDescription</key>
<string>This app needs access to contacts to let you select recipients</string>

Android Configuration

Add the following permissions to your AndroidManifest.xml:

<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />

Basic Usage

Import the Plugin

import { Contacts } from '@capgo/capacitor-contacts';

Request Permissions

const requestPermissions = async () => {
const permission = await Contacts.requestPermissions();
console.log('Permission status:', permission.contacts);
};

Get All Contacts

const getAllContacts = async () => {
const result = await Contacts.getContacts({
projection: {
name: true,
phones: true,
emails: true,
image: true
}
});
console.log('Contacts:', result.contacts);
};

Search Contacts

const searchContacts = async (query: string) => {
const result = await Contacts.getContacts({
projection: {
name: true,
phones: true,
emails: true
},
query: query
});
console.log('Search results:', result.contacts);
};

Create Contact

const createContact = async () => {
const newContact = {
name: {
given: 'John',
family: 'Doe'
},
phones: [{
type: 'mobile',
number: '+1234567890'
}],
emails: [{
type: 'work',
address: 'john.doe@example.com'
}]
};
const result = await Contacts.createContact(newContact);
console.log('Contact created:', result);
};

Update Contact

const updateContact = async (contactId: string) => {
const updates = {
contactId: contactId,
name: {
given: 'Jane',
family: 'Doe'
}
};
await Contacts.updateContact(updates);
console.log('Contact updated');
};

Delete Contact

const deleteContact = async (contactId: string) => {
await Contacts.deleteContact({ contactId });
console.log('Contact deleted');
};

Complete Example

Here’s a complete example with a contact service:

import { Contacts } from '@capgo/capacitor-contacts';
interface Contact {
contactId: string;
name: {
display?: string;
given?: string;
family?: string;
};
phones?: Array<{ type: string; number: string }>;
emails?: Array<{ type: string; address: string }>;
image?: { base64String: string };
}
class ContactsService {
async checkPermissions(): Promise<boolean> {
const permission = await Contacts.checkPermissions();
if (permission.contacts === 'granted') {
return true;
}
const requested = await Contacts.requestPermissions();
return requested.contacts === 'granted';
}
async getAllContacts(): Promise<Contact[]> {
const hasPermission = await this.checkPermissions();
if (!hasPermission) {
throw new Error('Contacts permission denied');
}
const result = await Contacts.getContacts({
projection: {
name: true,
phones: true,
emails: true,
image: true
}
});
return result.contacts;
}
async searchContacts(query: string): Promise<Contact[]> {
if (!query || query.length < 2) {
return [];
}
const result = await Contacts.getContacts({
projection: {
name: true,
phones: true,
emails: true
},
query: query
});
return result.contacts;
}
async getContactById(contactId: string): Promise<Contact | null> {
const result = await Contacts.getContacts({
projection: {
name: true,
phones: true,
emails: true,
image: true,
organization: true,
birthday: true,
note: true,
urls: true,
postalAddresses: true
},
contactId: contactId
});
return result.contacts.length > 0 ? result.contacts[0] : null;
}
async createContact(contact: Partial<Contact>): Promise<string> {
const hasPermission = await this.checkPermissions();
if (!hasPermission) {
throw new Error('Contacts permission denied');
}
const result = await Contacts.createContact(contact);
return result.contactId;
}
async updateContact(contactId: string, updates: Partial<Contact>): Promise<void> {
await Contacts.updateContact({
contactId,
...updates
});
}
async deleteContact(contactId: string): Promise<void> {
await Contacts.deleteContact({ contactId });
}
formatPhoneNumber(phone: string): string {
// Remove non-numeric characters
const cleaned = phone.replace(/\D/g, '');
// Format as (XXX) XXX-XXXX
if (cleaned.length === 10) {
return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3, 6)}-${cleaned.slice(6)}`;
}
return phone;
}
getContactInitials(contact: Contact): string {
const given = contact.name?.given || '';
const family = contact.name?.family || '';
if (given && family) {
return `${given[0]}${family[0]}`.toUpperCase();
}
const display = contact.name?.display || '';
const parts = display.split(' ');
if (parts.length >= 2) {
return `${parts[0][0]}${parts[1][0]}`.toUpperCase();
}
return display.slice(0, 2).toUpperCase();
}
}
// Usage
const contactsService = new ContactsService();
// Get all contacts
const contacts = await contactsService.getAllContacts();
console.log('Contacts:', contacts);
// Search contacts
const results = await contactsService.searchContacts('john');
console.log('Search results:', results);
// Create contact
const newContactId = await contactsService.createContact({
name: { given: 'Jane', family: 'Smith' },
phones: [{ type: 'mobile', number: '+1234567890' }]
});
// Update contact
await contactsService.updateContact(newContactId, {
emails: [{ type: 'work', address: 'jane@example.com' }]
});

Understanding Projection

The projection parameter controls which fields to fetch:

const result = await Contacts.getContacts({
projection: {
name: true, // Contact name
phones: true, // Phone numbers
emails: true, // Email addresses
image: true, // Contact photo
organization: true, // Company/organization
birthday: true, // Birth date
note: true, // Notes
urls: true, // Websites
postalAddresses: true // Physical addresses
}
});

Tip: Only request fields you need to improve performance.

Contact Picker UI

Many apps prefer using the native contact picker:

const pickContact = async () => {
try {
const result = await Contacts.pickContact({
projection: {
name: true,
phones: true,
emails: true
}
});
if (result.contacts.length > 0) {
const contact = result.contacts[0];
console.log('Selected contact:', contact);
}
} catch (error) {
console.error('Contact picker cancelled or failed:', error);
}
};

Best Practices

  1. Request Minimal Permissions: Only request contacts permission when needed
  2. Use Projection: Only fetch fields you actually use
  3. Handle Denials: Provide fallback when permissions denied
  4. Cache Wisely: Contacts can change, don’t cache too long
  5. Respect Privacy: Be transparent about contact usage
  6. Async Operations: All contact operations are async

Common Issues

Permission Denied

const handlePermissionDenied = async () => {
const permission = await Contacts.checkPermissions();
if (permission.contacts === 'denied') {
// Show dialog explaining why permission is needed
showPermissionDialog();
// Direct user to settings
// On iOS: Settings > [App] > Contacts
// On Android: Settings > Apps > [App] > Permissions
}
};

Large Contact Lists

const loadContactsInBatches = async () => {
// Get count first (lightweight)
const projection = { name: true }; // Minimal projection
// Implement pagination if needed
const allContacts = await Contacts.getContacts({ projection });
// Process in chunks
const chunkSize = 100;
for (let i = 0; i < allContacts.contacts.length; i += chunkSize) {
const chunk = allContacts.contacts.slice(i, i + chunkSize);
await processContactChunk(chunk);
}
};

Next Steps

  • Explore the API Reference for complete method documentation
  • Check out the example app for advanced usage
  • See the tutorial for complete implementation examples