A simple, modern TypeScript library for VCard 3.0 (RFC 2426) with browser and Node.js support
npm install vcard-tsA simple, modern TypeScript library for creating VCard 3.0 (RFC 2426) formatted strings. Zero dependencies, fully typed, and works in both browser and Node.js environments.
- ✅ RFC 2426 Compliant - Strictly follows the VCard 3.0 specification
- 🎯 Simple API - No complex builder patterns, just plain objects
- 🔒 Fully Typed - Complete TypeScript type definitions
- 📦 Zero Dependencies - Lightweight and fast
- 🌐 Universal - Works in browser and Node.js
- ✨ Modern - ESM + CommonJS, tree-shakeable
- 🧪 Well Tested - Comprehensive test coverage
- 🍏 iOS URL Labels - Supports multiple URLs with human-friendly labels in Apple Contacts
While VCard 4.0 exists, as of right now 3.0 remains the most widely supported format.
``bash`
npm install vcard-ts
`typescript
import { formatVCard, type VCard } from 'vcard-ts';
const vcard: VCard = {
version: '3.0',
formattedName: 'John Doe',
name: {
familyName: 'Doe',
givenName: 'John',
},
};
const vcardString = formatVCard(vcard);
console.log(vcardString);
`
Output:
``
BEGIN:VCARD
VERSION:3.0
FN:John Doe
N:Doe;John;;;
END:VCARD
Some legacy vCard consumers expect or require a CHARSET=... parameter on text properties.
Note: CHARSET is not defined in RFC 2426 itself; this library supports it as a legacy interoperability option.
By default, the formatter assumes UTF-8 and does not emit any CHARSET parameters.
`typescript
import { formatVCard, type VCard } from 'vcard-ts';
const vcard: VCard = {
version: '3.0',
formattedName: 'José García',
name: {
familyName: 'García',
givenName: 'José',
},
};
const vcardString = formatVCard(vcard);
console.log(vcardString);
`
Output:
``
BEGIN:VCARD
VERSION:3.0
FN:José García
N:García;José;;;
END:VCARD
If you need legacy interoperability, you can explicitly declare a charset via formatter options:
`typescript`
const vcardString = formatVCard(vcard, { charset: 'ISO-8859-1' });
You can include media either as a URI reference or inline base64 data:
`typescript
// URI Reference
const vcardWithPhoto: VCard = {
version: '3.0',
formattedName: 'Jane Smith',
name: { familyName: 'Smith', givenName: 'Jane' },
photo: {
uri: 'https://example.com/photo.jpg',
mediaType: 'JPEG',
},
};
// Inline Base64 (must be pre-encoded)
const vcardWithInlinePhoto: VCard = {
version: '3.0',
formattedName: 'Jane Smith',
name: { familyName: 'Smith', givenName: 'Jane' },
photo: {
value: 'base64EncodedDataHere...',
encoding: 'b',
mediaType: 'JPEG',
},
};
`
Some Android/Google Contacts imports can mis-parse folded lines (RFC 2426 line folding with CRLF + space), especially for structured properties like ADR. If you see parts of the address appear as a name suffix or the address disappears, disable folding:
`typescript
import { formatVCard, type VCard } from 'vcard-ts';
const vcard: VCard = {
version: '3.0',
formattedName: 'Jane Smith',
name: { familyName: 'Smith', givenName: 'Jane' },
addresses: [
{
street: '100 Tech Plaza',
locality: 'San Francisco',
region: 'CA',
postalCode: '94105',
country: 'United States of America',
types: ['work'],
},
],
};
// Keeps ADR on a single line (no folded continuations)
const vcardString = formatVCard(vcard, { foldLines: false });
`
The library automatically escapes special characters according to RFC 2426:
`typescript
const vcard: VCard = {
version: '3.0',
formattedName: 'Test; User, with\\special chars',
name: {
familyName: 'User',
givenName: 'Test',
},
note: 'Line 1\nLine 2',
};
// Special characters are automatically escaped:
// ; -> \;
// , -> \,
// \ -> \\
// newline -> \n
`
Many consumers (including iOS Contacts) support multiple URL entries but won’t display a meaningful label unless you use Apple’s extension fields.
This library supports that via urls, emitting itemN.URL + itemN.X-ABLabel. The item1., item2., etc. prefix is the vCard group syntax: it scopes multiple related lines under the same group name. So that item1.X-ABLabel can be associated with item1.URL.
The X-ABLabel property is an Apple-specific X- extension used by iOS Contacts to display a human-friendly label for the preceding itemN.URL field (e.g. “LinkedIn”, “Website”). This is usually not supported on Android.
`typescript
import { formatVCard, type VCard } from 'vcard-ts';
const vcard: VCard = {
version: '3.0',
formattedName: 'Michael Wolz',
name: { familyName: 'Wolz', givenName: 'Michael' },
urls: [
{ value: 'https://www.linkedin.com/in/michaelwolz', type: 'LinkedIn' },
{ value: 'https://michaelwolz.de', type: 'Website' },
],
};
console.log(formatVCard(vcard));
`
Output (excerpt):
``
item1.URL:https://www.linkedin.com/in/michaelwolz
item1.X-ABLabel:LinkedIn
item2.URL:https://michaelwolz.de
item2.X-ABLabel:Website
Notes:
- iOS: shows both URLs and the labels.
- Android: typically shows both URLs, but often ignores the X-ABLabel labels.
- vCard 4.0 supports richer, standardized URL typing, but vCard 3.0 tends to have broader real-world compatibility; this is a practical workaround.
The main VCard type representing a complete vCard object.
Required Fields:
- version: '3.0' - Must be "3.0"formattedName: string
- - Formatted name (FN property)name: Name
- - Structured name (N property)
Optional Fields:
See the TypeScript definitions for the complete list of optional fields including:
- Identification: nickname, photo, birthdayaddresses
- Delivery Addressing: , labelsphones
- Telecommunications: , emails, mailertimezone
- Geographical: , geotitle
- Organizational: , role, logo, agent, organizationcategories
- Explanatory: , note, productId, revision, sortString, sound, url, uidurl
- URLs: (single) and urls (multiple + optional iOS labels)class
- Security: , key
`typescript
function formatVCard(vcard: VCard, options?: { charset?: 'UTF-8' | 'ISO-8859-1' | 'US-ASCII' }): string;
// You can also disable RFC line folding for Android/Google Contacts compatibility:
// formatVCard(vcard, { foldLines: false })
`
Converts a VCard object into an RFC 2426 compliant VCard 3.0 string.
Parameters:
- vcard: VCard - The VCard object to formatoptions?: { charset?: 'UTF-8' | 'ISO-8859-1' | 'US-ASCII'; fold?: boolean }
- - Optional formatting optionscharset
- - (Legacy) Charset to declare via CHARSET parameters on text properties. If omitted, UTF-8 is assumed and no CHARSET is emitted.foldLines
- - Whether to apply RFC 2426 line folding (75 characters, CRLF + space). Default: true. Set to false if Android/Google Contacts mis-parses folded ADR lines.
Returns:
- string - RFC 2426 compliant VCard string with CRLF line endings
Features:
- Automatic text escaping for special characters
- Line folding for lines longer than 75 characters
- CHARSET parameters on text properties (legacy; only emitted when explicitly requested)
- Proper formatting of all VCard 3.0 properties
- Date/DateTime formatting
`bashInstall dependencies
npm install
Works in all modern browsers and Node.js 16+.
Contributions are welcome! Please ensure all tests pass and follow the existing code style.
- RFC 2426 - vCard MIME Directory Profile
- MIME Directory Profile (RFC 2425)