Installation
npx shadcn@latest add @pixeldosa/media-resultRequires `cn` at `@/lib/utils`. Render the media yourself and give images real alt text. Pass `credentials.attached: false` honestly — the absence is the useful signal.
Usage
"use client";
import { MediaResult } from "@/components/ui/media-result";
<MediaResult
title="Bottle on stone, warm light"
aspectRatio="4 / 5"
madeBy="Made with Imagen 4 · today at 14:02"
credentials={{ attached: true, detail: "Content Credentials + invisible watermark", href: verifyUrl }}
actions={[{ id: "use", label: "Use in the post", primary: true }, { id: "download", label: "Download" }]}
onAction={handle}
>
<img src={url} alt="A dark bottle on a stone surface in warm light" className="size-full object-cover" />
</MediaResult>
Why it exists
Since August 2026, the EU AI Act's Article 50 and California's SB 942 both require machine-readable disclosure of AI-generated media, and the big generators now sign their output with Content Credentials and an invisible watermark. The interface should say so, next to the result — and say when it hasn't.
Features
- Provenance in words, beside the media: what made it, when, and whether credentials are attached.
- Unsigned is a visible state. When credentials are missing the frame says so in the waiting colour, rather than staying quiet.
- A link to verify, when you have one.
- Your own note for usage rights, and actions with one primary.
- The layout never jumps: set
aspectRatioto the output's shape.
Variants
Signed
Bottle on stone, warm light
Made with Imagen 4 · today at 14:02
Marked as AI-made · Content Credentials + invisible watermark
Not signed yet
Draft key visual
Made with an internal model · 12 minutes ago
No content credentials attached yet
Don't publish until credentials are attached.
Props
| Prop | Type | Notes |
|---|---|---|
children | ReactNode | Required. The media itself; give images real alt text. |
title | string | What it is, in the user's words. |
aspectRatio | string | Defaults to "1 / 1". |
madeBy | string | Model and time, formatted by you. |
credentials | { attached, detail?, href? } | Pass attached: false honestly. |
note | string | Usage rights, or your own caveat. |
actions / onAction | { id, label, primary? }[] | At most one primary. |
Accessibility
- A
figurewith afigcaption, so the title and provenance are associated with the media by the platform. - The provenance state is text, not only an icon or a colour.
- The caption sits outside the media box, so it's never baked into a screenshot of the image.
When not to use
- Work still in progress: use
Generation Job. - A grid of candidates: use
Variation Grid. - A general "AI generated" badge on text: use
AI Disclosure.
why it's built this way
Engineering Notes
`credentials` is a small object rather than a boolean because the three questions a team has are different: is it signed, what was applied, and where can it be checked. Treating the unsigned case as a visible state in the waiting colour — instead of simply omitting the line — is the deliberate inversion here: products that only show a badge when signing succeeds make an unsigned file indistinguishable from a file nobody thought about, which is precisely the one that must not ship under the 2026 disclosure rules. The frame is a `figure` with a `figcaption`, so provenance and title are associated with the media by the platform rather than by proximity, and the whole caption stays outside the media box so it is never baked into a screenshot of the image itself.
how it moves, and when it doesn't
Motion Notes
Nothing animates: the work is finished, and this system reserves motion for a machine that is still busy. Only the standard instant colour changes on the action buttons, dropped under `prefers-reduced-motion`.