Docs

Dev tools

Expo SDKExpo CLIExpo MCPExpo GoSnackOrbit

Services

WorkflowsBuildSubmitUpdateHostingLaunchObserve
new
Simulators
preview

Explore

ChangelogExpo Services (EAS)ContactAI
EnterprisePricingBlog
50K
Log in
Sign up

Site footer

Expo

Newsletter

Stay in touch with all things Expo

Product

  • Star us on GitHub
  • Expo CLI on GitHub
  • Expo Services (EAS)
  • EAS CLI on GitHub
  • Expo Go
  • Expo Orbit
  • Snack

Resources

  • Documentation
  • Blog
  • Changelog
  • Support
  • Trust Center
  • Join Discord

Solutions

  • Enterprise
  • Startup
  • Solo devs
  • React web devs
  • E-commerce
  • Crypto
  • Finserv
  • QSR

Company

  • Home
  • Pricing
  • Customers
  • Consultants
  • About
  • Branding
  • Careers

Legal

  • Terms of service
  • Acceptable use policy
  • Privacy policy
  • Privacy explained
  • Cookie policy
  • Security & Compliance
  • Enterprise trust
  • Community guidelines
© 2026 650 Industries, Inc.
All systems operational

March 12, 2026::Product

The next generation of Expo APIs: MediaLibrary and Contacts

SDK 55 brings the next iteration of our core libraries. Shared Objects & Shared Refs fundamentally change how to interact with native data with these libraries.

Wiktor Smaga

Wiktor Smaga

Engineering

The Next Generation of Expo APIs: MediaLibrary and Contacts

Contents

  • MediaLibrary@Next
  • Core concept
  • Direct Access to Properties
  • Powerful queries
  • Contacts@Next
  • Core concept
  • Granular updates
  • Performant querying
  • .patch() + .getDetails() = <3
  • Check out the new documentation

We believe Shared Objects and Shared Refs are the future for Expo libraries, because they provide deeper integration between modules without unnecessary coupling. To read more about shared objects and shared refs, check out this article.

In SDK 54 we updated the File System with the new object-oriented API. Now, it's time for Contacts and MediaLibrary to get their upgrade. The updated versions of the libraries are available as expo-contacts/next and expo-media-library/next. We will continue to bring these features to other libraries in upcoming SDK releases!

Now, let’s dive into what has changed in the contacts and media library packages.

MediaLibrary@Next

Core concept

The new API is built around a simple idea: Album and Asset are now classes that work as proxies for the native data. They hold a reference to the objects in the system gallery to easily fetch metadata.

To create a new asset, you must pass a local URI to the static Asset.create() function. This command adds a file to the gallery and returns a shared object:

Once you have an asset, managing it is much more intuitive:

Direct Access to Properties

The common bottleneck in the old API was the size of the object returned by the getAssetInfoAsync() function. You couldn't simply check the location without fetching a massive plain object containing every single property. MediaLibrary@Next provides granular getters that let you access exactly what you need. No more fetching the entire metadata object just to check the dimensions of an image.

Powerful queries

MediaLibrary@Next introduces a new way to filter media. The new Query class draws on the builder pattern, allowing you to create more specific queries in a highly readable way.

It supports the following predicates: gt, gte, lte, lt, eq, within and album - along with orderBy for sorting and limit/offset for pagination.

Contacts@Next

Core concept

We designed the new API based on the following assumptions:

Contact objects in Contacts@Next hold a reference to the system contacts and contain a lot of async functions to fetch or update the underlying system contacts state. Unlike in the old API, you don't have to pass IDs manually in every function call, and you can rely solely on the update function called on an instance of a specific contact, which avoids a lot of easy-to-make mistakes.

To get started, you can create, fetch, or pick a contact using static methods:

Granular updates

No one likes dealing with massive JavaScript objects just to update a single property. For instance, if you wanted to add a new email address to a contact in the legacy API, you had to modify and send back the entire object:

Now you can perform the same operation with just one line of code:

And you can do this with every contact property! Look how easy it is to set a profile picture now:

Performant querying

The same goes for querying - if you want to fetch just one specific field, you can use granular getters:

To get more than just one property, use getDetails. Simply pass an array of the fields you are interested in:

And if you are building a list of contacts, there is a more efficient function for this: getAllDetails(). Under the hood, it uses a single system API call, making it very performant. It also supports different types of filtering as an optional parameter.

Additionally, objects returned by these functions have a narrowed type based on the fields requested in the arguments. TypeScript will warn you if you try to access a field that hasn't been fetched.

.patch() + .getDetails() = <3

The new version of the library includes a patch() method - similar to the one in the HTTP protocol - which applies partial modifications. It skips undefined properties, so you only update the fields you actually want to change.

It has been designed to work smoothly with getDetails(): while getDetails() fetches only requested fields, patch() updates only the defined ones. This is perfect for forms where you only change one or two fields:

If you want to completely replace the contact properties, you can still use the update method. Unlike patch, it overrides the entire contact:

Check out the new documentation

Since this is the next iteration of these libraries, your input is more important than ever. We’re open and extremely grateful for any feedback, GitHub issues, feature requests, or tweets you want to send our way.

We also invite you to check out the new docs, where you will find examples of how the APIs work in action!

Shared Objects
Shared Refs

Share article

Related articles

All posts →
How to upgrade to Expo SDK 55

February 26, 2026

How to upgrade to Expo SDK 55

Home screen widgets and Live Activities in Expo

March 4, 2026

Home screen widgets and Live Activities in Expo

// Create an asset from a local URI
const asset = await Asset.create('file:///path/to/photo.png');
// Fetch an album and add the asset directly
const album = await Album.get('Holiday 2026');
await album.add(asset);
// Or create a new album and add the asset in one go
const album = await Album.create('Holiday 2026', [asset]);
await asset.getLocation(); // { longitude: 50, latitude: 20 }
await asset.getShape(); // { width: 100, height: 100 }
// Need everything? You can still do that:
await asset.getInfo();
const assets = await new Query()
.eq(AssetField.MEDIA_TYPE, MediaType.IMAGE)
.lte(AssetField.HEIGHT, 1080)
.within(AssetField.WIDTH, [920, 960, 1080])
.orderBy(AssetField.CREATION_TIME)
.limit(20)
.offset(10)
.exe();
// Create a new contact
const contact = await Contact.create({
givenName: 'Andrew',
familyName: 'Jones',
phones: [
{
label: 'mobile',
number: '+12123456789',
},
],
});
// Fetch existing contact
const [contact] = await Contact.getAll({ limit: 1 });
// Or pick one using the system UI
const contact = await Contact.presentPicker();
// THE OLD WAY
const updatedContact = {
...contact,
emails: [...(contact.emails || []), { address: 'contacts-next@expo.dev', label: 'work' }],
};
await Contacts.updateContactAsync(updatedContact);
// THE NEW WAY
await contact.addEmail({ address: 'contacts-next@expo.dev', label: 'work' });
const result = await ImagePicker.launchImageLibraryAsync();
await contact.setImage(result.assets[0].uri);
const givenName = await contact.getGivenName();
await contact.getDetails([ContactField.EMAILS, ContactField.FULL_NAME]);
// This is a static module function.
await Contact.getAllDetails([ContactField.EMAILS, ContactField.FULL_NAME]);
const details = await contact.getDetails([ContactField.GIVEN_NAME]);
console.log(details.givenName); // ✅ "John"
console.log(details.phones); // ❌ TypeScript will show a warning
const details = await contact.getDetails([ContactField.EMAILS, ContactField.GIVEN_NAME]);
details.givenName = 'John';
await contact.patch(details); // Only updates the givenName in the system.
await contact.update({
givenName: 'New Name',
familyName: 'New Surname',
// Other fields will be cleared
});