Skip to content

Provider configuration

Multi-provider apps wire API keys and client construction in app-local modules — not via a ProviderFactory export from @media-sdk/core. Copy the pattern from the starters or reference demo; keep it in your repo so you control env naming and defaults.

Canonical createMediaClient recipe

Both examples/basic-react and examples/basic-react-native ship the same ~30-line factory. Add a src/createMediaClient.ts file:

ts
import {
  ApiKeyProvider,
  MemoryCache,
  PixabayMediaClient,
  PexelsMediaClient,
  type Cache,
  type MediaClient,
} from "@media-sdk/core";

export type MediaProviderId = "pexels" | "pixabay";

export interface CreateMediaClientOptions {
  provider: MediaProviderId;
  apiKey: string;
  cache?: Cache;
}

export function createMediaClient(
  options: CreateMediaClientOptions,
): MediaClient {
  const auth = new ApiKeyProvider(options.apiKey);
  const cache = options.cache ?? new MemoryCache(60_000);

  if (options.provider === "pexels") {
    return new PexelsMediaClient(auth, { cache });
  }

  return new PixabayMediaClient(auth, { cache });
}

provider === "pexels" branching is allowed here (and in a provider selector) — it chooses which client to construct. It must not gate UI features; use getCapabilities or useMediaCapabilities for that.

Environment variable matrix

VariableNode / serverVite (web)Expo (React Native)
Pexels API keyPEXELS_API_KEYVITE_PEXELS_API_KEYEXPO_PUBLIC_PEXELS_API_KEY
Pixabay API keyPIXABAY_API_KEYVITE_PIXABAY_API_KEYEXPO_PUBLIC_PIXABAY_API_KEY
Default provider (optional)MEDIA_PROVIDERVITE_MEDIA_PROVIDEREXPO_PUBLIC_MEDIA_PROVIDER

Values for the default provider: pexels or pixabay. When omitted, starters default to Pexels.

Copy .env.example from the starter that matches your bundler:

bash
# Web (examples/basic-react/.env.example)
VITE_PEXELS_API_KEY=
VITE_PIXABAY_API_KEY=
VITE_MEDIA_PROVIDER=pexels

# Native (examples/basic-react-native/.env.example)
EXPO_PUBLIC_PEXELS_API_KEY=
EXPO_PUBLIC_PIXABAY_API_KEY=
EXPO_PUBLIC_MEDIA_PROVIDER=pexels

Restart the dev server after changing env files (Vite and Expo do not hot-reload env vars).

Provider selection vs capability-driven UI

Split responsibilities into two layers:

LayerResponsibilityAllowed patterns
App shellRead env keys, pick provider, build MediaClient, wrap MediaProviderprovider === in createMediaClient; ProviderSelector updates provider state
PresentationTabs, filters, curated feed, preview actionsuseMediaCapabilities() / getCapabilities(client) only

App shell (web starter)

MediaAppShell holds provider state, builds a client with useMemo, and passes it to MediaProvider. When the user switches providers, the client prop changes and hooks re-fetch with the new capabilities:

tsx
import { useMemo, useState } from "react";
import { MediaProvider } from "@media-sdk/react";

import { createMediaClient, type MediaProviderId } from "./createMediaClient";

export function MediaAppShell({
  initialProvider,
  pexelsApiKey,
  pixabayApiKey,
}: {
  initialProvider: MediaProviderId;
  pexelsApiKey: string;
  pixabayApiKey: string;
}) {
  const [provider, setProvider] = useState(initialProvider);
  const apiKey = provider === "pexels" ? pexelsApiKey : pixabayApiKey;

  const client = useMemo(
    () => createMediaClient({ provider, apiKey }),
    [provider, apiKey],
  );

  return (
    <MediaProvider client={client}>
      <App provider={provider} onProviderChange={setProvider} />
    </MediaProvider>
  );
}

The same structure exists in examples/basic-react-native with Expo env reads.

Presentation layer

Gate tabs and filters with capabilities, not provider strings:

tsx
import { useMediaCapabilities } from "@media-sdk/react";

function SearchTabs() {
  const caps = useMediaCapabilities();

  return (
    <>
      <PhotosTab />
      <VideosTab />
      {caps.operations.curatedPhotos && <CuratedTab />}
    </>
  );
}

See Provider comparison for the full anti-pattern list.

providerConfig.ts helper

Starters also include providerConfig.ts for labels, env var names, and readProviderApiKeys() / resolveDefaultProvider(). Keep provider-specific strings in this module so App.tsx stays provider-neutral.

Curated photos (app recipe)

getCuratedPhotos is Pexels-only. The SDK does not export useCuratedPhotos — copy the app-local hook from examples/basic-react/src/useCuratedPhotos.ts (or the native starter equivalent) if you need a curated tab:

tsx
// App-local pattern — not part of @media-sdk/react
import { useMediaCapabilities } from "@media-sdk/react";
import { useCuratedPhotos } from "./useCuratedPhotos";

function CuratedPanel() {
  const caps = useMediaCapabilities();
  const { data, loading, error } = useCuratedPhotos({ enabled: caps.operations.curatedPhotos });

  if (!caps.operations.curatedPhotos) return null;
  // render grid...
}

Gate the tab with caps.operations.curatedPhotos before calling the hook.

Runtime provider swap

Changing MediaProvider's client prop (as in apps/web and the starters) is the supported way to swap providers at runtime. Hooks abort in-flight searches, reset state, and useMediaCapabilities() reflects the new client immediately.