MediaFrame
StableAn image in a fixed shape, with a placeholder while it loads, a message if it fails, and an optional caption and AI label.
Also known as image, aspect ratio, image frame, thumbnail
Install with npm install @tattva/ui (setup), or copy the code from the kit in Downloads.
Ratios
<div className="grid w-full grid-cols-2 gap-3 sm:grid-cols-4">
<MediaFrame src={sampleImage(30)} alt="Harbor at low tide" ratio="1:1" caption="1:1" />
<MediaFrame src={sampleImage(90)} alt="Terraced hillside" ratio="4:3" caption="4:3" />
<MediaFrame src={sampleImage(160)} alt="Quiet street" ratio="16:9" caption="16:9" />
<MediaFrame src={sampleImage(210)} alt="Lake under cloud" ratio="3:2" caption="3:2" />
</div>Pick the shape that fits the content. The frame crops the image to fill it.
When to use it
Shows an image without making the page jump. It saves the space first, fills it with a placeholder, and offers Retry if the image fails.
Made of: Frame · Placeholder · Image · Error message · AI label · Caption
Use it for
- A photo or generated image in a reply or a card.
- An image that may load slowly or fail.
- An AI-made image that needs a label saying where it came from.
Not for
- Several images in a gallery: use MediaGrid.
- A drawing for an empty or error screen: use Illustration.
- An AI-made option the person picks from: use VariantTile.
Do and avoid
Do
- Describe what the image shows.
- Give the main color when you have it, so the placeholder matches the image.
- Label AI-made images with the visible AI label.
- Use one shape for a group of images.
Avoid
- Do not use the AI request as the image description. Describe what is in the picture.
- Do not leave out the description unless the image is only decoration.
- Do not hide the AI label to make an image look cleaner.
- Do not let images pick their own size. Without a set shape, the page jumps.
On a phone
- The frame fills the width of its container and keeps its ratio, so the height follows the width.
- The AI label sits at the bottom left of the image, and with provenance it opens a popover from a button that has a 44px tap area on touch screens.
- The Retry button keeps its size on touch screens and has a 44px tap area. Keep errorLabel short so the error tile fits a small frame.
Seen in products
- ThumbHash and BlurHash placeholders encode an image into a few bytes. A 1x1 BlurHash is the average color, which is enough to reserve layout, and explicit dimensions prevent layout shift.
- Major image generators embed C2PA or IPTC metadata and SynthID is an invisible watermark. The best practice reported is a visible human label plus machine-readable metadata.
- Adobe Content Credentials uses three levels of progressive disclosure: a small cr icon, then a popover with creator, date and tool, then full provenance.
Properties
| Prop | Type | Default | Description |
|---|---|---|---|
| srcrequired | string | none | Image URL. |
| altrequired | string | none | What the image shows. Ignored when decorative is set. |
| decorative | boolean | none | Renders alt="". |
| ratio | "1:1" | "4:3" | "16:9" | "3:2" | "4:3" | Fixed aspect ratio that is reserved before load. |
| dominantColor | string | none | Average color as a CSS color. Used as the placeholder. Without it a shimmer shows while loading. |
| caption | string | none | Wraps the frame in figure and adds a figcaption. |
| aiGenerated | boolean | none | Shows the AI label on the image. |
| aiLabel | string | "AI-generated" | Text of the label. |
| provenance | Omit<ProvenancePopoverProps, "label"> | none | When set, the label opens a ProvenancePopover with these details. |
| onRetry | () => void | none | Called when Retry is pressed. The image is requested again either way. |
| status | "loading" | "error" | none | Forces a state, for previews or controlled loading. |
| errorLabel | string | "Image could not load" | Text in the error tile. |
| retryLabel | string | "Retry" | Text of the retry button. |
| flush | boolean | false | Edge to edge inside a card: no corners or border of its own, so the card's corners clip the picture. |
| className | string | none | Extra classes for the frame wrapper. |
"Show code under the preview gives the code for the example on the stage."
In Figma
In Figma, one MediaFrame switches all of this from its properties panel, across 32 variants. Each property sets the React prop beside it, so design and code use the same names.
| Figma property | React | Values |
|---|---|---|
| Ratio | ratio | 1:1, 4:3, 16:9, 3:2 |
| Ai generated | aiGenerated | On or off |
| Status | status | Loading, Error |
| Flush | flush | On or off |
| Caption | caption | Any text |
Tokens
Change one of these and every part that uses it follows.
--surface-sunken--border--dur-base--lime-soft--radius-control--radius-card
Built in
- The description is required. With decorative, the image gets an empty one and screen readers skip it.
- With a caption, the image and caption form one figure, so screen readers read them together.
- If the image fails, the message is real text and Retry is a real button.
- The AI label is written in words. With provenance, it is a button: Enter, Space or a tap opens it, and Escape closes it.
- The placeholder shimmer and the fade-in stop when people turn off motion in their system settings.
What you need to do
- Write alt text that says what the image shows, not how it was made.
- Set decorative only when the image adds nothing the text does not.
- Announce a failed load yourself if people must know. The error message appears without being announced.
Built toward WCAG 2.2 AA and checked automatically; no independent audit yet. Read the accessibility guide