Live demo
The reference application at apps/web in the monorepo demonstrates the Headless Media SDK end to end. It consumes only published package exports — no direct packages/*/src imports.
What it demonstrates
| Feature | SDK pieces used |
|---|---|
| Multi-provider switching | ProviderSelector + createMediaClient → PexelsMediaClient / PixabayMediaClient |
| Capability-driven UI | useMediaCapabilities gates tabs and filters — never provider === checks |
| Photo and video search | useMediaSearch, useMediaVideos |
| Submit-to-search pattern | enabled defer on hooks |
| Pagination | Pagination + nextPage / previousPage |
| Photo and video previews | PhotoPreview, VideoPreview |
| View tracking | client.trackView on preview open |
| Curated photos tab | useCuratedPhotos (demo-local hook) gated by capabilities.operations.curatedPhotos |
| Orientation and size filters | Gated by capabilities.photoFilters.orientation and capabilities.photoFilters.size |
| Loading, error, and empty states | LoadingState, ErrorState |
| Missing API key handling | AppSetup with per-provider env instructions |
Architecture
flowchart LR
web["apps/web"]
react["@media-sdk/react"]
ui["@media-sdk/ui-react"]
core["@media-sdk/core"]
web --> react
web --> ui
react --> core
ui --> core| Layer | Role |
|---|---|
apps/web | Demo shell: provider selector, env wiring, createMediaClient, search UI composition |
@media-sdk/react | MediaProvider, useMediaSearch, useMediaVideos, useMediaCapabilities |
@media-sdk/ui-react | Presentational components (grids, previews, pagination, states) |
@media-sdk/core | PexelsMediaClient, PixabayMediaClient, types, and HTTP transport |
Provider switch flow: MediaAppShell holds the active provider in state. Selecting a new provider builds a fresh client via createMediaClient, passes it to MediaProvider, and remounts MediaApp with key={provider} so tab, filter, and preview state reset cleanly.
Prerequisites
Environment setup
Copy the example env file and add your API keys:
cp apps/web/.env.example apps/web/.envEdit apps/web/.env:
VITE_PEXELS_API_KEY=your_pexels_api_key_here
VITE_PIXABAY_API_KEY=your_pixabay_api_key_here
# Optional default when both keys are set (pexels | pixabay)
VITE_MEDIA_PROVIDER=pexels| Variable | Required | Description |
|---|---|---|
VITE_PEXELS_API_KEY | When using Pexels | Pexels API key exposed to the Vite client bundle |
VITE_PIXABAY_API_KEY | When using Pixabay | Pixabay API key exposed to the Vite client bundle |
VITE_MEDIA_PROVIDER | No | Default provider when both keys are set (pexels or pixabay; defaults to pexels) |
At least one key must be set. Without any key, the app renders AppSetup with setup instructions. If the selected provider's key is missing, AppSetup shows which env var to set for that provider (and still shows the provider selector when another key is configured).
Run locally
From the monorepo root, install dependencies:
pnpm installBuild SDK packages before the first run (or after package changes):
pnpm --filter @media-sdk/core build
pnpm --filter @media-sdk/react build
pnpm --filter @media-sdk/ui-react buildStart the dev server:
pnpm --dir apps/web devOpen the URL printed by Vite (typically http://localhost:5173).
Production build
pnpm --dir apps/web buildOutput is written to apps/web/dist.
Using published npm packages
The demo in the monorepo uses workspace packages during development. To build a similar app with published npm packages:
pnpm create vite my-media-app --template react-ts
cd my-media-app
pnpm add @media-sdk/core@^0.3.0 @media-sdk/react@^0.3.0 @media-sdk/ui-react@^0.3.0Copy .env with VITE_PEXELS_API_KEY (and optionally VITE_PIXABAY_API_KEY) and follow the Quick Start guide.
Feature walkthrough
Provider switching
Use the Provider dropdown in the header to switch between Pexels and Pixabay at runtime — no page reload. Providers without a configured API key appear disabled in the selector.
Switching providers:
- Swaps the
MediaClientinsideMediaProvider - Remounts the search UI so incompatible tab and filter state is cleared
- Re-renders capability-gated controls from
useMediaCapabilities()(curated tab, size filter, etc.)
Provider names appear only in the selector label and env setup text — feature visibility is never gated with if (provider === "pexels"). See Capabilities and Provider comparison.
Search
Submit a query from the search bar. Photo results use useMediaSearch; video results use useMediaVideos when the Videos tab is active. The hook uses enabled: submittedQuery.length > 0 so no request fires until the user submits.
Curated tab
When the active client supports curated photos (capabilities.operations.curatedPhotos), a Curated tab appears. This tab is hidden automatically for providers that do not support the operation (for example, Pixabay).
Filters
- Orientation — shown only when
capabilities.photoFilters.orientationistrue - Size — shown only when
capabilities.photoFilters.sizeistrue(Pexels only)
Filter values are passed to useMediaSearch via photoFilters. Switching to Pixabay hides unsupported controls automatically.
Pagination
Pagination controls call nextPage / previousPage from the active hook. Changing video pages clears any open video preview.
Preview and trackView
Selecting a photo or video opens a preview dialog. trackPhotoPreviewOpen / trackVideoPreviewOpen call client.trackView once per preview open — not on search or pagination. See Events.
Error, empty, and loading states
- Loading —
LoadingStatewhile a request is in flight - Error —
ErrorStatewith the API error message - Empty — inline message when a search returns no results
- Unconfigured —
AppSetupwhen no API keys are set, or when the selected provider's key is missing
Testing
# Web app tests
pnpm --dir apps/web test
# Monorepo-wide
pnpm test
pnpm typecheckIntegration tests under apps/web/src/integration/ exercise full search → preview → trackView flows against mocked HTTP responses.
Next steps
- Quick Start — build your own app from scratch
- Provider comparison — capability matrix and anti-patterns
- React hooks —
useMediaSearch,useMediaVideos,enabled - UI components — prop tables for all components
- Installation — package install and TypeScript setup