Migrate to expo-media-library/next
Edit page
Migrate from the legacy expo-media-library API to the new class-based expo-media-library/next API with Asset, Album, and Query.
For the complete documentation index, see llms.txt. Use this file to discover all available pages.
The expo-media-library/next API is now stable. The legacy expo-media-library module is deprecated. Migrate to expo-media-library/next to benefit from the new API and future fixes.
The new API replaces the function-based MediaLibrary.getAssetsAsync({ ... }) style with Asset, Album, and Query classes. Albums and assets are now represented as class instances that hold only the ID of the native asset. Asset properties are async getters instead of pre-fetched fields. Query replaces the getAssetsAsync function with a chainable builder pattern.
Installation
Install the SDK-compatible package that includes expo-media-library/next:
-Â npx expo install expo-media-libraryImporting the new API
Import from expo-media-library/next:
import { Asset, Album, Query } from 'expo-media-library/next';
Assets
Create an asset from a file
// Before await MediaLibrary.saveToLibraryAsync(localUri); // or, to get a reference back: const asset = await MediaLibrary.createAssetAsync(localUri); // After const asset = await Asset.create(localUri);
saveToLibraryAsync is not available in the new API. Use Asset.create, which saves the file to the library and returns an Asset instance.
Query assets
// Before const { assets } = await MediaLibrary.getAssetsAsync({ mediaType: MediaLibrary.MediaType.photo, first: 20, sortBy: [['creationTime', false]], }); // After const assets = await new Query() .eq(AssetField.MEDIA_TYPE, MediaType.IMAGE) .limit(20) .orderBy({ key: AssetField.CREATION_TIME, ascending: false }) .exe();
Query returns an array of Asset instances directly. There is no assets wrapper or endCursor. Pagination is handled by chaining .limit() and .offset().
Read asset properties
// Before const info = await MediaLibrary.getAssetInfoAsync(asset); console.log(info.filename, info.width, info.height); // After, individual getters const filename = await asset.getFilename(); const width = await asset.getWidth(); const height = await asset.getHeight(); const mediaType = await asset.getMediaType(); // After, all properties at once const info = await asset.getInfo();
Properties are accessed through async getters instead of pre-fetched fields. Use getInfo() to retrieve all properties at once as an AssetInfo object.
Read EXIF data
// Before const info = await MediaLibrary.getAssetInfoAsync(asset); const exif = info.exif; // After const exif = await asset.getExif();
Delete assets
// Before await MediaLibrary.deleteAssetsAsync([asset]); // After, single asset await asset.delete(); // After, multiple assets await Asset.delete([asset1, asset2]);
Albums
Get an album by name
// Before const album = await MediaLibrary.getAlbumAsync('MyAlbum'); // After const album = await Album.get('MyAlbum'); if (album) { // album found }
Get all albums
// Before const albums = await MediaLibrary.getAlbumsAsync(); // After const albums = await Album.getAll();
Create an album
// Before const album = await MediaLibrary.createAlbumAsync('MyNewAlbum', asset, false); // After const album = await Album.create('MyNewAlbum', [asset]);
Get all assets in an album
// Before const { assets } = await MediaLibrary.getAssetsAsync({ album: album.id }); // After const assets = await album.getAssets();
Get album title
// Before, title was a synchronous property but required fetching the full album object first const album = await MediaLibrary.getAlbumAsync('MyAlbum'); console.log(album.title); // After const title = await album.getTitle();
Add assets to an album
// Before await MediaLibrary.addAssetsToAlbumAsync([asset], album, false); // After await album.add([asset]);
Remove assets from an album (iOS only)
// Before await MediaLibrary.removeAssetsFromAlbumAsync(assets, album); // After await album.removeAssets(assets);
Delete an album
// Before await MediaLibrary.deleteAlbumsAsync([album], false); // After, single album await album.delete(); // After, multiple albums await Album.delete([album1, album2]);
Permissions
The permission hooks and functions are available under the same names. The only change is that presentPermissionsPickerAsync was renamed to presentPermissionsPicker.
// Before await MediaLibrary.presentPermissionsPickerAsync(mediaTypes); // After await presentPermissionsPicker(mediaTypes);
requestPermissionsAsync, getPermissionsAsync, and usePermissions are unchanged.
Listening for changes
// Before const subscription = MediaLibrary.addListener(event => { ... }); subscription.remove(); // After const subscription = addListener(event => { ... }); subscription.remove(); // Remove all listeners at once removeAllListeners();
The listener event shape is unchanged.
Breaking semantic changes
- Asset properties are now async getters (
getFilename(),getWidth(), ...) instead of synchronous fields on a result object. Useasset.getInfo()to retrieve all properties at once. - The
Asyncsuffix is dropped. The entire library is asynchronous. Queryreplaces thegetAssetsAsyncoptions bag. There is noendCursor/hasNextPage. Use.limit()and.offset()for pagination.- Operations on albums and assets are now methods on
AlbumandAssetinstances instead of free functions that accept an ID or reference. saveToLibraryAsyncis replaced byAsset.create, which returns anAssetinstance.getMomentsAsync,albumNeedsMigrationAsync, andmigrateAlbumIfNeededAsyncare removed with no replacement. You can safely delete any calls to these functions.
Reference
See the full API reference for expo-media-library/next.