Skip to content

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.

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.

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.
}
}
  • Secure context. MDN documents select() as available only in secure contexts (HTTPS).
  • User gesture. MDN states that select() requires a user gesture for the returned Promise to resolve.
  • Not available in Web Workers. MDN notes the Contact Picker API is not exposed via WorkerNavigator.

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.

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);
}

MDN’s select() reference documents error conditions the call can throw or reject with:

  • TypeError — the requested properties list 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().

  • Feature-detect with 'contacts' in navigator && 'ContactsManager' in window before calling select().
  • 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, and SecurityError — with try/catch or 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.