Skip to content

UI components (React Native)

@media-sdk/ui-native@^0.1.0 provides reusable presentational React Native components. Components render UI from props — they never call the API, manage search state, or read from MediaProvider.

bash
pnpm add @media-sdk/ui-native@^0.1.0 react react-native

Peer dependencies: React 18 or 19, react-native ≥ 0.71.

Presentational-only philosophy

LayerResponsibility
Your appData fetching, search state, pagination, capability gating
@media-sdk/nativeHooks and MediaProvider
@media-sdk/ui-nativeUI rendering — props in, JSX out

Components do not:

  • Call searchPhotos, searchVideos, or any MediaClient method
  • Read from MediaProvider context
  • Branch on provider identity
  • Fetch data on mount

Public API

All 11 exported components:

ts
import {
  SearchBar,
  MediaTabs,
  PhotoCard,
  PhotoGrid,
  PhotoPreview,
  VideoCard,
  VideoGrid,
  VideoPreview,
  Pagination,
  LoadingState,
  ErrorState,
  type MediaTab,
} from "@media-sdk/ui-native";

Internal utilities (getPhotoSrc, getPlayableVideoLink) are not exported.

Component catalog

ComponentPurpose
SearchBarControlled search input and submit button
MediaTabsPhotos / Videos tab switcher
PhotoCardSingle photo thumbnail with press handler
PhotoGridVirtualized photo grid (FlatList)
PhotoPreviewModal overlay for a selected photo
VideoCardSingle video thumbnail with duration
VideoGridVirtualized video grid (FlatList)
VideoPreviewModal with poster; optional renderVideo for playback
PaginationPrevious / next page controls
LoadingStateAccessible loading message
ErrorStateAccessible error message

PropTypeRequiredDescription
valuestringYesCurrent input value
onChange(value: string) => voidYesCalled when text changes (onChangeText)
onSubmit() => voidYesCalled on submit button press or keyboard submit

Unlike web SearchBar, there is no FormEventonSubmit is a plain callback.

tsx
<SearchBar
  value={query}
  onChange={setQuery}
  onSubmit={() => setSubmittedQuery(query)}
/>

MediaTabs

PropTypeRequiredDescription
activeTabMediaTabYes"photos" or "videos"
onTabChange(tab: MediaTab) => voidYesTab selection handler
ts
type MediaTab = "photos" | "videos";

Gate the Videos tab in your app with useMediaCapabilities().operations.searchVideos.


PhotoCard

PropTypeRequiredDescription
photoPhotoYesPhoto from @media-sdk/core
onSelect(photo: Photo) => voidYesPress handler

Uses Image with photo.src.medium ?? small ?? original.


PhotoGrid

PropTypeRequiredDescription
photosPhoto[]YesPhotos to display
onPhotoSelect(photo: Photo) => voidYesSelection handler
numColumnsnumberNoDefault 2 — passed to FlatList
contentContainerStyleStyleProp<ViewStyle>NoFlatList content container style

Renders nothing when photos.length === 0.


PhotoPreview

PropTypeRequiredDescription
photoPhotoYesPhoto to preview
onClose() => voidYesClose handler (back button + close control)

Uses RN Modal with full-size image (large ?? medium ?? small ?? original).


VideoCard

| Prop | Type | Required | Description | | --- | --- | --- | | video | Video | Yes | Video from @media-sdk/core | | onSelect | (video: Video) => void | Yes | Press handler |

Thumbnail from video.image URL.


VideoGrid

PropTypeRequiredDescription
videosVideo[]YesVideos to display
onVideoSelect(video: Video) => voidYesSelection handler
numColumnsnumberNoDefault 2
contentContainerStyleStyleProp<ViewStyle>NoFlatList content style

VideoPreview

PropTypeRequiredDescription
videoVideoYesVideo to preview
onClose() => voidYesClose handler
renderVideo(video, source) => ReactNodeNoApp-supplied player

When renderVideo is omitted, shows poster image and fallback text. Supply a player from react-native-video, expo-av, or similar — not bundled by the SDK.

tsx
<VideoPreview
  video={video}
  onClose={close}
  renderVideo={(_v, src) =>
    src ? <Video source={{ uri: src }} /> : null
  }
/>

Pagination

PropTypeRequiredDescription
pagenumberYesCurrent page number
hasPreviousbooleanYesEnable previous button
hasNextbooleanYesEnable next button
loadingbooleanNoDisable buttons while loading
accessibilityLabelstringYesNav container label (replaces web ariaLabel)
onPrevious() => voidYesPrevious page handler
onNext() => voidYesNext page handler

LoadingState

PropTypeRequiredDescription
messagestringYesLoading text

Includes ActivityIndicator and accessibilityLiveRegion="polite" where supported.


ErrorState

PropTypeRequiredDescription
messagestringYesError text

Uses accessibilityRole="alert".


Styling

Components ship with minimal StyleSheet defaults. Override via optional style / contentContainerStyle props on grids. No theming system in v0.1.0.

Accessibility

RN accessibility props — not aria-*:

Web (ui-react)Native (ui-native)
aria-labelaccessibilityLabel
aria-selectedaccessibilityState=
role="alert"accessibilityRole="alert"

Next steps