Album creation, management, and asset organization with support for both user-created albums and smart albums (iOS), including comprehensive album operations and platform-specific migration features.
Retrieves all user-created albums with optional smart album inclusion on iOS.
/**
* Queries for user-created albums with optional smart album inclusion
* @param options - Album query options (optional)
* @returns Promise resolving to array of Album objects
*/
function getAlbumsAsync(options?: AlbumsOptions): Promise<Album[]>;
interface AlbumsOptions {
/** Include smart albums in results - iOS only */
includeSmartAlbums?: boolean;
}Usage Examples:
import * as MediaLibrary from "expo-media-library";
// Get all user-created albums
const albums = await MediaLibrary.getAlbumsAsync();
console.log(`Found ${albums.length} albums`);
// Include smart albums (iOS only)
const allAlbums = await MediaLibrary.getAlbumsAsync({
includeSmartAlbums: true
});
// List album details
albums.forEach(album => {
console.log(`Album: ${album.title} (${album.assetCount} assets)`);
});Searches for a specific album by its title.
/**
* Finds album by name
* @param title - Album name to search for
* @returns Promise resolving to Album object or null if not found
*/
function getAlbumAsync(title: string): Promise<Album | null>;Usage Examples:
import * as MediaLibrary from "expo-media-library";
// Find specific album
const myAlbum = await MediaLibrary.getAlbumAsync('My Photos');
if (myAlbum) {
console.log(`Found album with ${myAlbum.assetCount} assets`);
} else {
console.log('Album not found');
}
// Check if album exists before operations
const targetAlbum = await MediaLibrary.getAlbumAsync('Screenshots');
if (!targetAlbum) {
// Create album if it doesn't exist
const newAlbum = await MediaLibrary.createAlbumAsync('Screenshots');
}Creates a new album with optional initial asset.
/**
* Creates new album with optional initial asset
* @param albumName - Name for the new album
* @param asset - Initial asset or asset ID to add (optional)
* @param copyAsset - Whether to copy asset instead of moving (Android only, optional)
* @param initialAssetLocalUri - Local URI for initial asset (optional)
* @returns Promise resolving to the created Album object
*/
function createAlbumAsync(
albumName: string,
asset?: AssetRef,
copyAsset?: boolean,
initialAssetLocalUri?: string
): Promise<Album>;Usage Examples:
import * as MediaLibrary from "expo-media-library";
// Create empty album
const emptyAlbum = await MediaLibrary.createAlbumAsync('My New Album');
// Create album with initial asset
const photos = await MediaLibrary.getAssetsAsync({ first: 1 });
const albumWithPhoto = await MediaLibrary.createAlbumAsync(
'Photo Album',
photos.assets[0]
);
// Create album with asset copy (Android)
const copiedAlbum = await MediaLibrary.createAlbumAsync(
'Copied Photos',
photos.assets[0],
true // Copy instead of move on Android
);
// Create album with local URI
const savedPhoto = await MediaLibrary.createAssetAsync('file:///path/to/photo.jpg');
const albumFromUri = await MediaLibrary.createAlbumAsync(
'Saved Photos',
savedPhoto,
false,
'file:///path/to/photo.jpg'
);Deletes one or more albums from the media library.
/**
* Deletes albums from the media library
* @param albums - Single album or array of albums/album IDs to delete
* @param assetRemove - Also delete contained assets (iOS only, optional)
* @returns Promise resolving to boolean indicating success
*/
function deleteAlbumsAsync(
albums: AlbumRef[] | AlbumRef,
assetRemove?: boolean
): Promise<boolean>;Usage Examples:
import * as MediaLibrary from "expo-media-library";
// Delete single album
const album = await MediaLibrary.getAlbumAsync('Old Album');
if (album) {
const success = await MediaLibrary.deleteAlbumsAsync(album);
console.log(`Delete ${success ? 'successful' : 'failed'}`);
}
// Delete multiple albums
const allAlbums = await MediaLibrary.getAlbumsAsync();
const emptyAlbums = allAlbums.filter(album => album.assetCount === 0);
const deleteSuccess = await MediaLibrary.deleteAlbumsAsync(emptyAlbums);
// Delete album and its assets (iOS only)
const albumToDelete = await MediaLibrary.getAlbumAsync('Temporary Album');
if (albumToDelete) {
await MediaLibrary.deleteAlbumsAsync(albumToDelete, true); // Delete assets too
}
// Delete by album IDs
const albumIds = ['album-id-1', 'album-id-2'];
await MediaLibrary.deleteAlbumsAsync(albumIds);Adds one or more assets to an existing album.
/**
* Adds assets to an album
* @param assets - Single asset or array of assets/asset IDs to add
* @param album - Target album or album ID
* @param copy - Copy assets instead of moving (Android only, optional)
* @returns Promise resolving to boolean indicating success
*/
function addAssetsToAlbumAsync(
assets: AssetRef[] | AssetRef,
album: AlbumRef,
copy?: boolean
): Promise<boolean>;Usage Examples:
import * as MediaLibrary from "expo-media-library";
// Add single asset to album
const photos = await MediaLibrary.getAssetsAsync({
mediaType: MediaLibrary.MediaType.photo,
first: 1
});
const album = await MediaLibrary.getAlbumAsync('My Photos');
if (album && photos.assets.length > 0) {
const added = await MediaLibrary.addAssetsToAlbumAsync(photos.assets[0], album);
}
// Add multiple assets
const recentPhotos = await MediaLibrary.getAssetsAsync({
mediaType: MediaLibrary.MediaType.photo,
first: 10,
sortBy: [MediaLibrary.SortBy.creationTime, false]
});
const photoAlbum = await MediaLibrary.getAlbumAsync('Recent Photos');
if (photoAlbum) {
await MediaLibrary.addAssetsToAlbumAsync(recentPhotos.assets, photoAlbum);
}
// Copy assets instead of moving (Android)
await MediaLibrary.addAssetsToAlbumAsync(
recentPhotos.assets,
photoAlbum,
true // Copy on Android
);
// Add newly created asset
const newAsset = await MediaLibrary.createAssetAsync('file:///path/to/new/photo.jpg');
const targetAlbum = await MediaLibrary.getAlbumAsync('New Photos');
if (targetAlbum) {
await MediaLibrary.addAssetsToAlbumAsync(newAsset, targetAlbum);
}Removes assets from an album without deleting the assets themselves.
/**
* Removes assets from album without deleting the assets
* @param assets - Single asset or array of assets/asset IDs to remove
* @param album - Source album or album ID
* @returns Promise resolving to boolean indicating success
*/
function removeAssetsFromAlbumAsync(
assets: AssetRef[] | AssetRef,
album: AlbumRef
): Promise<boolean>;Usage Examples:
import * as MediaLibrary from "expo-media-library";
// Remove single asset from album
const album = await MediaLibrary.getAlbumAsync('My Album');
const albumAssets = await MediaLibrary.getAssetsAsync({
album: album,
first: 1
});
if (album && albumAssets.assets.length > 0) {
await MediaLibrary.removeAssetsFromAlbumAsync(albumAssets.assets[0], album);
}
// Remove multiple assets
const assetsToRemove = await MediaLibrary.getAssetsAsync({
album: album,
first: 5
});
await MediaLibrary.removeAssetsFromAlbumAsync(assetsToRemove.assets, album);
// Remove assets by ID
const assetIds = ['asset-1', 'asset-2', 'asset-3'];
await MediaLibrary.removeAssetsFromAlbumAsync(assetIds, album);
// Clean up album by removing old assets
const oldAssets = await MediaLibrary.getAssetsAsync({
album: album,
createdBefore: new Date('2022-01-01'),
first: 100
});
if (oldAssets.assets.length > 0) {
await MediaLibrary.removeAssetsFromAlbumAsync(oldAssets.assets, album);
}Access to moment albums that group assets by location and time.
/**
* Fetches moments (asset groups by place/time)
* @platform ios
* @returns Promise resolving to array of moment Album objects
*/
function getMomentsAsync(): Promise<Album[]>;Usage Examples:
import * as MediaLibrary from "expo-media-library";
import { Platform } from "react-native";
if (Platform.OS === 'ios') {
const moments = await MediaLibrary.getMomentsAsync();
moments.forEach(moment => {
console.log(`Moment: ${moment.title}`);
console.log(`Assets: ${moment.assetCount}`);
console.log(`Period: ${new Date(moment.startTime)} - ${new Date(moment.endTime)}`);
if (moment.locationNames) {
console.log(`Locations: ${moment.locationNames.join(', ')}`);
}
});
}Support for migrating albums to scoped storage directories on Android R+.
/**
* Checks if album needs migration to scoped storage
* @platform android R+
* @param album - Album or album ID to check
* @returns Promise resolving to boolean indicating if migration is needed
*/
function albumNeedsMigrationAsync(album: AlbumRef): Promise<boolean>;
/**
* Migrates album to scoped storage directories
* @platform android R+
* @param album - Album or album ID to migrate
* @returns Promise resolving when migration is complete
*/
function migrateAlbumIfNeededAsync(album: AlbumRef): Promise<void>;Usage Examples:
import * as MediaLibrary from "expo-media-library";
import { Platform } from "react-native";
if (Platform.OS === 'android' && Platform.Version >= 30) {
const albums = await MediaLibrary.getAlbumsAsync();
for (const album of albums) {
const needsMigration = await MediaLibrary.albumNeedsMigrationAsync(album);
if (needsMigration) {
console.log(`Migrating album: ${album.title}`);
await MediaLibrary.migrateAlbumIfNeededAsync(album);
console.log('Migration complete');
}
}
}interface Album {
/** Unique identifier for the album */
id: string;
/** Album title/name */
title: string;
/** Estimated number of assets in the album */
assetCount: number;
/** Album type - iOS only */
type?: AlbumType;
/** Start time for moment albums - iOS only */
startTime: number;
/** End time for moment albums - iOS only */
endTime: number;
/** Approximate location for moment albums - iOS only */
approximateLocation?: Location;
/** Location names for moment albums - iOS only */
locationNames?: string[];
}
/**
* Album types available on iOS
* @platform ios
*/
type AlbumType =
| 'album' // User-created album
| 'moment' // Moment album (grouped by time/location)
| 'smartAlbum'; // Smart album (Favorites, Screenshots, etc.)
interface Location {
/** Latitude coordinate */
latitude: number;
/** Longitude coordinate */
longitude: number;
/** Altitude in meters (optional) */
altitude?: number;
/** Accuracy in meters (optional) */
accuracy?: number;
}
type AlbumRef = Album | string;
type AssetRef = Asset | string;albumId property