Contact Picker API: letting users share selected contacts, not the whole address book
Published
In one line: The Contact Picker API lets a web app ask the user to pick one or more contacts from their address book through a browser-native picker UI, sharing only the specific contacts and fields the user chooses — never the full contact list.
Why a picker instead of bulk access
Section titled “Why a picker instead of bulk access”The Contact Picker API allows users to select entries from their contact list and share
limited details of the selected entries with a website or application. navigator.contacts
exposes a ContactsManager, and ContactsManager.select() opens the picker the browser
controls; the user decides which contacts to select and which of the requested properties
(name, email, tel, address, icon) to release. MDN lists common use cases as
selecting a contact to message via email or chat, choosing a contact’s phone number for
VOIP, or discovering which contacts already use a given social platform.
Using it
Section titled “Using it”const props = ['name', 'email', 'tel'];const opts = { multiple: true };
async function getContacts() { try { const contacts = await navigator.contacts.select(props, opts); handleResults(contacts); } catch (ex) { // select() can throw — see "What goes wrong" below. }}props lists which fields to request; getProperties() reports which of those fields the
current browser actually supports, so a site can adjust its request before calling
select():
async function checkProperties() { const supported = await navigator.contacts.getProperties(); if (supported.includes('email')) { // Safe to request "email" in props. }}Requirements
Section titled “Requirements”- Secure context. MDN documents
select()as available only in secure contexts (HTTPS). - User gesture. MDN states that
select()requires a user gesture for the returnedPromiseto resolve. - Not available in Web Workers. MDN notes the Contact Picker API is not exposed via
WorkerNavigator.
Where it is supported
Section titled “Where it is supported”MDN marks the Contact Picker API as an experimental technology that is not part of
Baseline because it does not work in some of the most widely used browsers. Check the
Browser compatibility table on the MDN Contact_Picker_API and ContactsManager pages
before depending on it in production.
Feature detection
Section titled “Feature detection”async function shareContacts(props, opts) { if (!('contacts' in navigator && 'ContactsManager' in window)) { // Not supported here — fall back to a manual entry form instead. return null; } return navigator.contacts.select(props, opts);}What goes wrong
Section titled “What goes wrong”MDN’s select() reference documents error conditions the call can throw or reject with:
TypeError— the requestedpropertieslist is empty, or includes a property the browser does not support.InvalidStateError— the call is made from a browsing context that isn’t top-level, a contact picker is already open, or the picker otherwise fails to launch.SecurityError—select()was not triggered by user activation.
MDN’s own example handles them with try/catch; the promise-returning select() can
also be handled with .catch().
Practical checklist
Section titled “Practical checklist”- Feature-detect with
'contacts' in navigator && 'ContactsManager' in windowbefore callingselect(). - Call
select()only from a real user gesture — MDN documents that it requires one to resolve. - Call
getProperties()to confirm which fields the browser actually returns before relying on them. - Handle
TypeError,InvalidStateError, andSecurityError— withtry/catchor a.catch()on the returned promise. - Have a manual-entry fallback UI ready — MDN marks this API as not Baseline.
- Never assume persistent access — a fresh user action is required for every request.