MediaLibrary component
MediaLibrary is a grid of images and files to pick from, like the media screen of a CMS. It has search and type filters, one or many picks, Load more (also when scrolled into view), and uploading by a button or by dropping files. You supply the items and fetch more when asked; it does not load or store files itself. Put it in a Modal for an Insert image dialog.
Playground
Change a prop on the right and the preview updates. Switch to Code to copy what you see.
Picking an image
Each item is { id, name, thumbnail?, type?, size?, description? }. type is a MIME type and picks the icon and the type filter. By default the library filters items by the search text and the type itself. onAction runs on Enter or a double click, and onUpload adds an Upload button and accepts dropped files.
<MediaLibrary
items={media}
kinds={["image"]}
onAction={(item) => insertImage(item)}
onUpload={(files) => uploadAll(files)}
accept="image/*"
/>Many picks, filtered on a server
selectionMode multiple allows many picks (Shift+click for a range, Ctrl or Cmd+A for all). For a large library set filtering to none, listen to onSearchChange and onFilterChange, and pass the results as items with hasMore and onLoadMore for paging.
<MediaLibrary
items={page.items}
selectionMode="multiple"
selectedKeys={picked}
onSelectionChange={setPicked}
filtering="none"
onSearchChange={setQuery}
onFilterChange={setKind}
hasMore={page.hasMore}
onLoadMore={fetchNext}
isLoading={loading}
/>Props
The full public API for MediaLibrary. Every prop is stable and intentionally typed.
Data
| Prop | Type | Default | Description |
|---|---|---|---|
| items | MediaItem[] | — | { id, name, thumbnail?, type?, size?, description?, data? }. type is a MIME type; it picks the icon and the filter. size is in bytes. |
| selectedKeys / defaultSelectedKeys / onSelectionChange | string[] / (keys: string[]) => void | [] | The picked item ids, controlled or not. |
| onAction | (item: MediaItem) => void | — | Enter or a double click on an item (use this one). |
| filtering | "local" | "none" | local | local: the library filters items by search and type. none: you filter on a server and pass the results. |
| search / defaultSearch / onSearchChange | string / (query) => void | "" | The search text. |
| filter / defaultFilter / onFilterChange | "all" | MediaKind | all | The chosen type filter. |
| hasMore / onLoadMore / isLoading | boolean / () => void / boolean | — | Shows Load more and calls onLoadMore when it is clicked or scrolls into view. isLoading shows placeholders. |
| onUpload | (files: File[]) => void | — | Adds an Upload button and lets files be dropped on the library. |
| accept | string | — | File types for uploads, like an input's accept. |
items
MediaItem[]
default: —
{ id, name, thumbnail?, type?, size?, description?, data? }. type is a MIME type; it picks the icon and the filter. size is in bytes.
selectedKeys / defaultSelectedKeys / onSelectionChange
string[] / (keys: string[]) => void
default: []
The picked item ids, controlled or not.
onAction
(item: MediaItem) => void
default: —
Enter or a double click on an item (use this one).
filtering
"local" | "none"
default: local
local: the library filters items by search and type. none: you filter on a server and pass the results.
search / defaultSearch / onSearchChange
string / (query) => void
default: ""
The search text.
filter / defaultFilter / onFilterChange
"all" | MediaKind
default: all
The chosen type filter.
hasMore / onLoadMore / isLoading
boolean / () => void / boolean
default: —
Shows Load more and calls onLoadMore when it is clicked or scrolls into view. isLoading shows placeholders.
onUpload
(files: File[]) => void
default: —
Adds an Upload button and lets files be dropped on the library.
accept
string
default: —
File types for uploads, like an input's accept.
Behavior & states
| Prop | Type | Default | Description |
|---|---|---|---|
| selectionMode | "single" | "multiple" | single | Pick one item or many. Shift+click picks a range. |
selectionMode
"single" | "multiple"
default: single
Pick one item or many. Shift+click picks a range.
Appearance
| Prop | Type | Default | Description |
|---|---|---|---|
| kinds | MediaKind[] | every kind | Type filters offered after All: image, video, audio, document. [] hides them. |
| size | "sm" | "md" | "lg" | md | Thumbnail size. |
| classNames | Partial<Record<slot, string>> | — | Class per part: base, toolbar, search, filters, grid, item, thumbnail, name, footer. |
kinds
MediaKind[]
default: every kind
Type filters offered after All: image, video, audio, document. [] hides them.
size
"sm" | "md" | "lg"
default: md
Thumbnail size.
classNames
Partial<Record<slot, string>>
default: —
Class per part: base, toolbar, search, filters, grid, item, thumbnail, name, footer.
Content
| Prop | Type | Default | Description |
|---|---|---|---|
| emptyContent | ReactNode | No media yet | Shown when there are no items at all. |
| labels | Partial<MediaLibraryLabels> | English | library, search, filters, kinds, upload, dropFiles, loadMore, loading, empty, noResults(query), selected(count), clearSelection. Also set app-wide with LocaleProvider. |
emptyContent
ReactNode
default: No media yet
Shown when there are no items at all.
labels
Partial<MediaLibraryLabels>
default: English
library, search, filters, kinds, upload, dropFiles, loadMore, loading, empty, noResults(query), selected(count), clearSelection. Also set app-wide with LocaleProvider.
Accessibility
- The grid is a listbox of options, multi-selectable in multiple mode, each named by its file name and detail (photo.jpg, 240 KB).
- One tab stop for the grid. Arrow keys move in two directions (right-to-left pages swap left and right), Home and End go to the ends, Space toggles in multiple mode, Shift with arrows extends the pick, and Enter acts.
- The type filters are toggle buttons in a named group; the search box is named. Loading is announced and the library is marked busy while it loads.
- Uploading by dropping files also has the Upload button, so it never depends on dragging.
Best practices
- Thumbnails must be http(s) or blob: URLs, paths starting with /, ./ or ../, or base64 data URLs of common image types; anything else is not loaded and the type icon is shown.
- Give each item a unique, stable id.
- Check uploaded files on your server too; accept only guides the file picker.
- Translate the text with the labels prop or LocaleProvider.