Introduction
Components
- Accordion
- Action Sheet
- Alert
- Audio Player
- Audio Recorder
- Audio Waveform
- Avatar
- Badge
- BottomSheet
- Button
- Camera
- Camera Preview
- Card
- Carousel
- Checkbox
- Collapsible
- Color Picker
- Combobox
- Date Picker
- File Picker
- Gallery
- Hello Wave
- Icon
- Image
- Input
- Input OTP
- Link
- MediaPicker
- Mode Toggle
- Onboarding
- ParallaxScrollView
- Picker
- Popover
- Progress
- Radio
- ScrollView
- SearchBar
- Separator
- Share
- Sheet
- Skeleton
- Spinner
- Switch
- Table
- Tabs
- Text
- Toast
- Toggle
- Video
- View
Charts
import { MediaPicker } from '@/components/ui/media-picker';
import React from 'react';
export function MediaPickerDemo() {
return (
<MediaPicker
mediaType='all'
onSelectionChange={(assets) => {
console.log('Selected assets:', assets);
}}
onError={(error) => {
console.error('Media picker error:', error);
}}
/>
);
}
Installation
pnpm dlx bna-ui add media-picker
Usage
import { MediaPicker } from '@/components/ui/media-picker';
<MediaPicker
mediaType='all'
multiple={true}
maxSelection={5}
onSelectionChange={(assets) => console.log(assets)}
/>
Examples
Default
import { MediaPicker } from '@/components/ui/media-picker';
import React from 'react';
export function MediaPickerDemo() {
return (
<MediaPicker
mediaType='all'
onSelectionChange={(assets) => {
console.log('Selected assets:', assets);
}}
onError={(error) => {
console.error('Media picker error:', error);
}}
/>
);
}
Image Only
import { MediaPicker } from '@/components/ui/media-picker';
import { Image } from 'lucide-react-native';
import React from 'react';
export function MediaPickerImages() {
return (
<MediaPicker
mediaType='image'
buttonText='Select Images'
icon={Image}
variant='outline'
onSelectionChange={(assets) => {
console.log('Selected images:', assets);
}}
/>
);
}
Video Only
import { MediaPicker } from '@/components/ui/media-picker';
import { Video } from 'lucide-react-native';
import React from 'react';
export function MediaPickerVideos() {
return (
<MediaPicker
mediaType='video'
buttonText='Select Videos'
icon={Video}
variant='secondary'
onSelectionChange={(assets) => {
console.log('Selected videos:', assets);
}}
/>
);
}
Multiple Selection
import { MediaPicker } from '@/components/ui/media-picker';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { Plus } from 'lucide-react-native';
import React, { useState } from 'react';
export function MediaPickerMultiple() {
const [selectedCount, setSelectedCount] = useState(0);
return (
<View style={{ gap: 12 }}>
<MediaPicker
mediaType='all'
multiple={true}
maxSelection={5}
buttonText={`Select Media (${selectedCount}/5)`}
icon={Plus}
onSelectionChange={(assets) => {
setSelectedCount(assets.length);
console.log('Selected assets:', assets);
}}
/>
{selectedCount > 0 && (
<Text variant='caption'>
{selectedCount} item{selectedCount !== 1 ? 's' : ''} selected
</Text>
)}
</View>
);
}
Custom Gallery
import { MediaAsset, MediaPicker } from '@/components/ui/media-picker';
import { Folder } from 'lucide-react-native';
import React, { useState } from 'react';
export function MediaPickerGallery() {
const [selected, setSelected] = useState<MediaAsset[]>([]);
return (
<MediaPicker
mediaType='all'
gallery={true}
multiple={true}
maxSelection={8}
buttonText='Open Gallery'
icon={Folder}
variant='outline'
selectedAssets={selected}
onSelectionChange={setSelected}
/>
);
}
With Preview
import { MediaAsset, MediaPicker } from '@/components/ui/media-picker';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { ImageIcon } from 'lucide-react-native';
import React, { useState } from 'react';
export function MediaPickerPreview() {
const [assets, setAssets] = useState<MediaAsset[]>([]);
return (
<View style={{ gap: 16 }}>
<MediaPicker
mediaType='all'
multiple={true}
maxSelection={6}
showPreview={true}
previewSize={100}
buttonText='Add Media'
icon={ImageIcon}
selectedAssets={assets}
onSelectionChange={(newAssets) => {
setAssets(newAssets);
console.log('Assets with preview:', newAssets);
}}
/>
{assets.length > 0 && (
<View>
<Text variant='caption'>
{assets.length} item{assets.length !== 1 ? 's' : ''} selected
</Text>
<Text variant='caption'>
Types: {assets.map((a) => a.type).join(', ')}
</Text>
</View>
)}
</View>
);
}
Quality Settings
import { MediaPicker } from '@/components/ui/media-picker';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { Settings } from 'lucide-react-native';
import React from 'react';
export function MediaPickerQuality() {
return (
<View style={{ gap: 16 }}>
<View>
<Text variant='title' style={{ marginBottom: 8 }}>
High Quality
</Text>
<MediaPicker
mediaType='image'
quality='high'
buttonText='High Quality Images'
icon={Settings}
variant='outline'
size='sm'
onSelectionChange={(assets) => {
console.log('High quality assets:', assets);
}}
/>
</View>
<View>
<Text variant='title' style={{ marginBottom: 8 }}>
Medium Quality
</Text>
<MediaPicker
mediaType='image'
quality='medium'
buttonText='Medium Quality Images'
icon={Settings}
variant='secondary'
size='sm'
onSelectionChange={(assets) => {
console.log('Medium quality assets:', assets);
}}
/>
</View>
<View>
<Text variant='title' style={{ marginBottom: 8 }}>
Low Quality
</Text>
<MediaPicker
mediaType='image'
quality='low'
buttonText='Low Quality Images'
icon={Settings}
variant='ghost'
size='sm'
onSelectionChange={(assets) => {
console.log('Low quality assets:', assets);
}}
/>
</View>
</View>
);
}
API Reference
MediaPicker
The main component for selecting media from device gallery or camera.
Prop | Type | Default | Description |
---|---|---|---|
children | ReactNode | - | Custom trigger element. If not provided, uses default button. |
style | ViewStyle | - | Additional styles to apply to the container. |
size | ButtonSize | - | Size of the default button trigger. |
variant | ButtonVariant | - | Variant of the default button trigger. |
icon | ComponentType<LucideProps> | - | Icon for the default button trigger. |
disabled | boolean | false | Whether the media picker is disabled. |
mediaType | 'image' | 'video' | 'all' | 'all' | Type of media to allow selection. |
multiple | boolean | false | Whether to allow multiple selection. |
maxSelection | number | 10 | Maximum number of items that can be selected. |
quality | 'low' | 'medium' | 'high' | 'high' | Quality of selected media. |
buttonText | string | - | Text for the default button trigger. |
placeholder | string | - | Placeholder text (currently unused). |
gallery | boolean | false | Whether to show custom gallery modal. |
showPreview | boolean | true | Whether to show preview of selected media. |
previewSize | number | 80 | Size of preview thumbnails in pixels. |
selectedAssets | MediaAsset[] | [] | Controlled selected assets. |
onSelectionChange | (assets: MediaAsset[]) => void | - | Callback when selection changes. |
onError | (error: string) => void | - | Callback when an error occurs. |
MediaAsset
The interface for media assets returned by the picker.
Property | Type | Description |
---|---|---|
id | string | Unique identifier for the asset. |
uri | string | Local URI of the selected media. |
type | 'image' | 'video' | Type of the media asset. |
width | number? | Width of the media in pixels. |
height | number? | Height of the media in pixels. |
duration | number? | Duration in seconds for videos. |
filename | string? | Original filename of the media. |
fileSize | number? | File size in bytes. |
Permissions
The MediaPicker component requires the following permissions:
- iOS:
NSPhotoLibraryUsageDescription
in Info.plist - Android:
READ_EXTERNAL_STORAGE
permission
The component automatically requests these permissions when first used.
Features
- Multiple Media Types: Support for images, videos, or both
- Gallery Integration: Custom gallery modal or system picker
- Preview Support: Show thumbnails of selected media
- Quality Control: Adjustable media quality settings
- Batch Selection: Select multiple items with configurable limits
- Error Handling: Comprehensive error handling and callbacks
- Accessibility: Built-in accessibility features
- Theme Integration: Respects your app's theme colors
Accessibility
The MediaPicker component is built with accessibility in mind:
- Proper labeling for screen readers
- Keyboard navigation support
- High contrast support for buttons and indicators
- Semantic structure for better navigation
- Error announcements for screen readers
Notes
- The component uses
expo-image-picker
andexpo-media-library
for media selection - Permissions are automatically requested when needed
- Selected assets are stored in memory during the session
- Preview thumbnails are generated automatically
- Video duration and file size information is included when available