Media component
The media component shows an asset from the CDN in a way that fits its moderation status: the media for allowed assets, a notice and a reveal button for age-restricted ones, and an unavailable notice for blocked and purged ones. Widgets covers installing it, styling and translating.
Show an asset
Pass its project, ID and stored moderation status, or an asset from the API as asset, which also supplies its media type and stored preview (placeholder, average colour and size); props you set win over the asset's values. Pass canView when your app lets this viewer see age-restricted media:
import type { AssetModerationStatus } from "@calmlens/sdk/app";
import { CalmLensMedia } from "@calmlens/sdk/react";
type PostPhotoProps = {
projectId: string;
assetId: string;
status: AssetModerationStatus;
alt: string;
/** Width divided by height, which you store when the image is uploaded. */
aspectRatio: number;
canView: boolean;
onReveal: () => void;
};
export function PostPhoto({
projectId,
assetId,
status,
alt,
aspectRatio,
canView,
onReveal,
}: PostPhotoProps) {
return (
<CalmLensMedia
projectId={projectId}
assetId={assetId}
status={status}
canView={canView}
onReveal={onReveal}
alt={alt}
aspectRatio={aspectRatio}
widths={[400, 800]}
sizes="(min-width: 768px) 50vw, 100vw"
/>
);
}
| Status | What the component shows |
|---|---|
| allowed, limited or pending | The media, with the blurred placeholder until it loads |
| age-restricted | The placeholder with a notice and a reveal button until you pass canView, then the media with the allowAgeRestricted opt-in |
| blocked or purged | An unavailable notice, without requesting the media |
Any status, when an asset's content.status is removed | An unavailable notice, without requesting the media |
The reveal button calls onReveal, where your app runs its own check before rendering again with canView; Show sensitive media covers that decision. A media URL that fails to load also shows the unavailable notice.
- Images come from the asset's rendered resource, so the CDN serves the best format the browser accepts without a list of formats.
widthsandsizesadd asrcset, so browsers fetch the smallest image that fits. Each width is a separate image transform; see Lenses and transforms. lensandtransformapply a named lens or a direct transform.aspectRatio(orwidthandheight) keeps the box its final size while the image loads.placeholderHashtakes the asset'scontent.image.thumbhashand draws the placeholder in the browser, with no placeholder request. It also works for private assets and signed URLs.urltakes a signed URL from your server instead of building a public CDN URL, for private assets or when CalmLens must refuse age-restricted content to anyone your backend hasn't approved.typeisimage(default),videooraudio. Video and audio play the original file, with no poster or placeholder.
React Native
CalmLensMedia from @calmlens/sdk/react-native shows assets the same way with React Native's Image. React Native has no srcset, so the component measures itself and loads the smallest of widths that covers its width in device pixels. Give it a size with aspectRatio, width and height, or an asset that has them, or a style; otherwise it keeps a minimum height while the image loads.
React Native has no built-in video or audio player. Pass renderPlayer to play them, for example with expo-video; without it, video and audio show only their placeholder:
import { CalmLensMedia, type MediaAsset, type MediaPlayerSource } from "@calmlens/sdk/react-native";
import { useVideoPlayer, VideoView } from "expo-video";
import { StyleSheet } from "react-native";
function Player({ src }: MediaPlayerSource) {
const player = useVideoPlayer(src);
return <VideoView player={player} style={StyleSheet.absoluteFill} nativeControls />;
}
export function PostMedia({ asset, alt }: { asset: MediaAsset; alt: string }) {
return (
<CalmLensMedia
asset={asset}
widths={[320, 640, 1080]}
alt={alt}
renderPlayer={(source) => <Player {...source} />}
/>
);
}sizes, lazy, fetchPriority, position and controls are web-only and not available in React Native.