← All @molecule/* packages · App templates

@molecule/app-contacts

Native · native · App (browser) · v1.0.1 · Apache-2.0

Contacts access interface for molecule.dev

npm install @molecule/app-contacts

npm · Source on GitHub

How it works

@molecule/app-contacts bridges the native core to the native platform layer of the app.

import {
  getAll,
  getPermissionStatus,
  hasProvider,
  pick,
  requestPermission,
  formatDisplayName,
} from '@molecule/app-contacts'

async function importContacts(): Promise<string[]> {
  if (!hasProvider()) return [] // no provider wired — feature-gate the UI
  if ((await getPermissionStatus()) !== 'granted') {
    const status = await requestPermission() // from a user gesture
    if (status !== 'granted') return []
  }
  const contacts = await getAll({ sortBy: 'name' })
  return contacts.map((c) => formatDisplayName(c))
}

async function pickOne(): Promise<void> {
  const [selected] = await pick({ multiple: false })
  if (selected) console.log(formatDisplayName(selected))
}

Works with: @molecule/app-bond, @molecule/app-i18n

Reference

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

Contacts / address-book access interface for molecule.dev.

Framework-agnostic core for reading, searching, creating, updating, deleting, and picking device contacts through a swappable ContactsProvider, plus pure helpers (formatDisplayName, getPrimaryPhone, getPrimaryEmail, formatPhoneNumber, getInitials) that work on Contact objects from any provider.

Quick Start

import {
  getAll,
  getPermissionStatus,
  hasProvider,
  pick,
  requestPermission,
  formatDisplayName,
} from '@molecule/app-contacts'

async function importContacts(): Promise<string[]> {
  if (!hasProvider()) return [] // no provider wired — feature-gate the UI
  if ((await getPermissionStatus()) !== 'granted') {
    const status = await requestPermission() // from a user gesture
    if (status !== 'granted') return []
  }
  const contacts = await getAll({ sortBy: 'name' })
  return contacts.map((c) => formatDisplayName(c))
}

async function pickOne(): Promise<void> {
  const [selected] = await pick({ multiple: false })
  if (selected) console.log(formatDisplayName(selected))
}

Type

native

Installation

npm install @molecule/app-contacts @molecule/app-bond @molecule/app-i18n

API

Interfaces

Contact

Full contact record with name, phones, emails, addresses, organization, and metadata.

interface Contact {
  /** Contact ID */
  id: string
  /** Contact name */
  name: ContactName
  /** Phone numbers */
  phones?: PhoneNumber[]
  /** Email addresses */
  emails?: EmailAddress[]
  /** Postal addresses */
  addresses?: PostalAddress[]
  /** Organization info */
  organization?: Organization
  /** Birthday (ISO date string) */
  birthday?: string
  /** Note/memo */
  note?: string
  /** Photo as base64 data URL */
  photo?: string
  /** URLs (websites, social) */
  urls?: { url: string; label?: string }[]
  /** Custom fields */
  customFields?: Record<string, string>
}

ContactName

Structured name components for a contact (given, family, middle, prefix, suffix, display).

interface ContactName {
  /** Full display name */
  display?: string
  /** Given/first name */
  given?: string
  /** Middle name */
  middle?: string
  /** Family/last name */
  family?: string
  /** Name prefix (e.g., 'Mr.', 'Dr.') */
  prefix?: string
  /** Name suffix (e.g., 'Jr.', 'III') */
  suffix?: string
}

ContactPickerOptions

Contact picker options

interface ContactPickerOptions {
  /** Allow multiple selection */
  multiple?: boolean
  /** Fields to request */
  fields?: (keyof Contact)[]
}

ContactQueryOptions

Contact query options

interface ContactQueryOptions {
  /** Search query string */
  query?: string
  /** Fields to include in results */
  fields?: (keyof Contact)[]
  /** Sort by field */
  sortBy?: 'name' | 'created' | 'modified'
  /** Sort direction */
  sortOrder?: 'asc' | 'desc'
  /** Maximum results */
  limit?: number
  /** Results offset */
  offset?: number
}

ContactsCapabilities

Contacts capabilities

interface ContactsCapabilities {
  /** Whether contacts access is supported */
  supported: boolean
  /** Whether reading is supported */
  canRead: boolean
  /** Whether writing is supported */
  canWrite: boolean
  /** Whether contact picker is supported */
  hasPicker: boolean
  /** Whether photos are supported */
  supportsPhotos: boolean
  /** Maximum contacts that can be fetched */
  maxResults?: number
}

ContactsProvider

Contacts provider interface

interface ContactsProvider {
  /**
   * Get all contacts, optionally filtered and sorted.
   * @param options - Query options (search, fields, sorting, pagination).
   * @returns An array of Contact objects matching the query.
   */
  getAll(options?: ContactQueryOptions): Promise<Contact[]>

  /**
   * Get a single contact by its ID.
   * @param id - The contact ID to look up.
   * @returns The matching Contact, or null if not found.
   */
  getById(id: string): Promise<Contact | null>

  /**
   * Search contacts
   * @param query - Search query
   * @param options - Query options
   */
  search(query: string, options?: Omit<ContactQueryOptions, 'query'>): Promise<Contact[]>

  /**
   * Create a new contact
   * @param contact - Contact data
   */
  create(contact: ContactInput): Promise<Contact>

  /**
   * Update an existing contact
   * @param id - Contact ID
   * @param contact - Updated contact data
   */
  update(id: string, contact: Partial<ContactInput>): Promise<Contact>

  /**
   * Delete a contact
   * @param id - Contact ID
   */
  delete(id: string): Promise<void>

  /**
   * Open native contact picker
   * @param options - Picker options
   */
  pick(options?: ContactPickerOptions): Promise<Contact[]>

  /**
   * Get permission status
   */
  getPermissionStatus(): Promise<ContactsPermissionStatus>

  /**
   * Request permission
   */
  requestPermission(): Promise<ContactsPermissionStatus>

  /**
   * Open system settings for contacts permission
   */
  openSettings(): Promise<void>

  /**
   * Get the platform's contacts capabilities.
   * @returns The capabilities indicating which contacts features are supported.
   */
  getCapabilities(): Promise<ContactsCapabilities>
}

EmailAddress

An email address entry for a contact with label (home, work) and primary flag.

interface EmailAddress {
  /** Email address string */
  address: string
  /** Label (e.g., 'home', 'work') */
  label?: string
  /** Whether this is the primary email */
  isPrimary?: boolean
}

Organization

Contact organization

interface Organization {
  /** Company/organization name */
  company?: string
  /** Job title */
  title?: string
  /** Department */
  department?: string
}

PhoneNumber

A phone number entry for a contact with label (mobile, home, work) and primary flag.

interface PhoneNumber {
  /** Phone number string */
  number: string
  /** Label (e.g., 'mobile', 'home', 'work') */
  label?: string
  /** Whether this is the primary phone */
  isPrimary?: boolean
}

PostalAddress

A postal/mailing address for a contact (street, city, state, postal code, country).

interface PostalAddress {
  /** Street address line 1 */
  street?: string
  /** Street address line 2 */
  street2?: string
  /** City */
  city?: string
  /** State/province */
  state?: string
  /** Postal/ZIP code */
  postalCode?: string
  /** Country */
  country?: string
  /** Label (e.g., 'home', 'work') */
  label?: string
  /** Formatted address string */
  formatted?: string
}

Types

ContactInput

Contact creation/update data

type ContactInput = Omit<Contact, 'id'> & { id?: string }

ContactsPermissionStatus

Permission status

type ContactsPermissionStatus = 'granted' | 'denied' | 'limited' | 'prompt' | 'unsupported'

Functions

create(contact)

Create a new contact on the device.

function create(contact: ContactInput): Promise<Contact>
  • contact — The contact data to create.

Returns: The created Contact with its assigned ID.

deleteContact(id)

Delete a contact from the device.

function deleteContact(id: string): Promise<void>
  • id — The ID of the contact to delete.

Returns: A promise that resolves when the contact is deleted.

formatDisplayName(contact, t)

Format a contact's display name from their name parts. Falls back to "Unknown" if no name parts exist.

function formatDisplayName(
  contact: Contact,
  t?: (
    key: string,
    values?: Record<string, unknown>,
    options?: { defaultValue?: string },
  ) => string,
): string
  • contact — The contact to format.
  • t — Optional i18n translation function for the "Unknown" fallback.

Returns: The formatted display name string.

formatPhoneNumber(phone)

Format a phone number for display using basic US formatting. 10-digit numbers become "(xxx) xxx-xxxx", 11-digit numbers with leading 1 become "+1 (xxx) xxx-xxxx".

function formatPhoneNumber(phone: PhoneNumber): string
  • phone — The PhoneNumber to format.

Returns: The formatted phone number string.

getAll(options)

Get all contacts, optionally filtered and sorted.

function getAll(options?: ContactQueryOptions): Promise<Contact[]>
  • options — Query options (search, fields, sorting, pagination).

Returns: An array of Contact objects matching the query.

getById(id)

Get a single contact by its ID.

function getById(id: string): Promise<Contact | null>
  • id — The contact ID to look up.

Returns: The matching Contact, or null if not found.

getCapabilities()

Get the platform's contacts capabilities.

function getCapabilities(): Promise<ContactsCapabilities>

Returns: The capabilities indicating which contacts features are supported.

getInitials(contact)

Get initials from a contact's name (e.g., "JD" for "John Doe"). Falls back to "??" if no name is available.

function getInitials(contact: Contact): string
  • contact — The contact to extract initials from.

Returns: A 1-2 character uppercase string of initials.

getPermissionStatus()

Get the current contacts permission status.

function getPermissionStatus(): Promise<ContactsPermissionStatus>

Returns: The permission status: 'granted', 'denied', 'limited', 'prompt', or 'unsupported'.

getPrimaryEmail(contact)

Get the primary email address for a contact, falling back to the first email.

function getPrimaryEmail(contact: Contact): EmailAddress | undefined
  • contact — The contact to extract the email from.

Returns: The primary EmailAddress, or undefined if the contact has no emails.

getPrimaryPhone(contact)

Get the primary phone number for a contact, falling back to the first number.

function getPrimaryPhone(contact: Contact): PhoneNumber | undefined
  • contact — The contact to extract the phone number from.

Returns: The primary PhoneNumber, or undefined if the contact has no phone numbers.

getProvider()

Get the current contacts provider.

function getProvider(): ContactsProvider

Returns: The active ContactsProvider instance.

hasProvider()

Check if a contacts provider has been registered.

function hasProvider(): boolean

Returns: Whether a ContactsProvider has been bonded.

openSettings()

Open the system settings screen for contacts permissions.

function openSettings(): Promise<void>

Returns: A promise that resolves when the settings screen is opened.

pick(options)

Open the native contact picker dialog.

function pick(options?: ContactPickerOptions): Promise<Contact[]>
  • options — Picker options (multiple selection, requested fields).

Returns: An array of selected Contact objects.

requestPermission()

Request contacts permissions from the user.

function requestPermission(): Promise<ContactsPermissionStatus>

Returns: The resulting permission status after the request.

search(query, options)

Search contacts by name, email, phone, or other fields.

function search(query: string, options?: Omit<ContactQueryOptions, 'query'>): Promise<Contact[]>
  • query — The search query string.
  • options — Additional query options (fields, sorting, pagination).

Returns: An array of matching Contact objects.

setProvider(provider)

Set the contacts provider.

function setProvider(provider: ContactsProvider): void
  • provider — ContactsProvider implementation to register.

update(id, contact)

Update an existing contact on the device.

function update(id: string, contact: Partial<ContactInput>): Promise<Contact>
  • id — The ID of the contact to update.
  • contact — The partial contact data to merge with the existing contact.

Returns: The updated Contact.

Injection Notes

Requirements

Peer dependencies:

  • @molecule/app-bond ^1.0.1
  • @molecule/app-i18n ^1.0.1

Runtime Dependencies

  • @molecule/app-bond

  • @molecule/app-i18n

  • Every accessor THROWS until setProvider() is called — there is no web fallback and no prebuilt provider package ships with molecule (contacts access needs a native container). Gate all contact UI behind hasProvider() and supply your own ContactsProvider from your native runtime.

  • Browsers have no general address-book API. Do not "fall back to web": the closest thing (Contact Picker API) is Chromium-on-Android only, read-only, picker-only — getAll/create/update/delete cannot be implemented on web at all.

  • Request permission from a user gesture at the point of use and handle 'denied'/'limited' — a denied OS prompt is remembered; recovery is openSettings(), not another requestPermission() call.

  • Check getCapabilities() before offering write features: iOS supports 'limited' access where only a subset of contacts is visible.

Translations

Translation strings are provided by @molecule/app-locales-contacts.