← UniformCONTENT HISTORY

Update to Uniform

Snapshot Sep 30, 2026 · 23:15 UTC · version 1.0.0

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Working with Uniform image/media assets end to end — defining an `asset` parameter, rendering an image `src` with `imageFrom` (the graceful, future-proof default), reading other fields off the raw asset item, the stored asset-value shape, DAM library assets vs external `custom-url` assets, seeding/migrating asset values in compositions, and the traps around parameter type transitions. Use when a component needs an image/photo/video from Uniform, when converting a `text` URL field to an `asset` field, when you want responsive/transformed images with focal points.",
  "included_files": [
    {
      "relative_path": "agents/openai.yaml",
      "size_in_bytes": 170
    }
  ],
  "name": "uniform-assets",
  "skill_md_contents": "---\nname: uniform-assets\ndescription: >-\n  Working with Uniform image/media assets end to end — defining an `asset`\n  parameter, rendering an image `src` with `imageFrom` (the graceful,\n  future-proof default), reading other fields off the raw asset item, the stored\n  asset-value shape, DAM library assets vs external `custom-url` assets,\n  seeding/migrating asset values in compositions, and the traps around parameter\n  type transitions. Use when a component needs an image/photo/video from Uniform,\n  when converting a `text` URL field to an `asset` field, when you want\n  responsive/transformed images with focal points.\n---\n\n# Uniform assets\n\nHow to model, render, and author image/media assets in this Uniform.\n\n## 1. Choose the parameter type deliberately\n\n| Use | When |\n|-----|------|\n| `asset` parameter | Real content authors will pick/manage the image; you want the Canvas asset picker, DAM library, and image CDN/transforms. **Default for authored imagery.** |\n| `text` parameter (URL) | Quick/pragmatic cases, throwaway/demo data, or an image URL that's genuinely just a string the author pastes. Simpler, but no picker, no metadata, no CDN transforms. |\n\nDon't reach for `text` just to avoid the asset plumbing — the plumbing is small\n(below). Reach for `text` only when a bare URL really is the right model and a text input is the right editor experience.\n\n## 2. Define an asset parameter\n\nIn the component definition. Via MCP `mutateComponent` or `uniform-data/component/<type>.json`:\n\n```json\n{\n  \"id\": \"photo\",\n  \"name\": \"Photo\",\n  \"type\": \"asset\",\n  \"typeConfig\": { \"allowedTypes\": [\"image\"] },\n  \"guidance\": \"A square headshot image. If empty the card shows initials.\"\n}\n```\n\n- `typeConfig.allowedTypes` ∈ `[\"image\", \"video\", \"audio\", \"other\"]` (one or more).\n- The parameter `id` must match the key you destructure in the `.tsx`.\n\n## 3. Render an image `src` — `imageFrom` (the default)\n\n**Default to `imageFrom` for turning an asset parameter into an img `src`.**\nIt is the most future-proof choice:\n\n- Accepts a **raw asset item OR a bare URL string**, so it keeps working if the\n  parameter's source ever changes (DAM ⇄ `custom-url` ⇄ external).\n- **No-ops transforms** for non-image assets and anything outside the Uniform\n  Asset Library — external / other-DAM URLs pass through unchanged, so one code\n  path is correct for every source.\n- **Auto-applies the asset's focal point** plus your resize/fit *when the image is\n  a Uniform DAM asset*. Those features \"just start working\" after a move to the\n  DAM, with **no code change**.\n\nUse the `imageFrom` and `AssetParamValue` from `@uniformdev/assets`\n\n```ts\nimport { imageFrom, type AssetParamValue } from \"@uniformdev/assets\";\n```\n\n### For use with a `ComponentParameter<AssetParamValue>`\n\nWhen the parameter type you are working with is `ComponentParameter<AssetParamValue>`, first access the `.value`. It is an `AssetParamValue`, which is an array of `AssetParamValueItem`.\n\n```tsx\n// photo?: ComponentParameter<AssetParamValue>\nconst item = photo?.value?.[0];\n```\n\n### Build an img from a single `AssetParamValueItem`\n\nOnce you have a single item, build an img src and attributes from the `AssetParamValueItem` that `imageFrom` needs.\n\n```tsx\nconst src = item\n  ? imageFrom(item)\n      .transform({ width: 192, height: 192, fit: \"cover\" })\n      .url()\n  : undefined;\n\nif (!src) return null;\nreturn <img src={src} alt={item?.fields.title?.value ?? \"\"} width={192} height={192} />;\n```\n\nSafety rules:\n\n- **Give `imageFrom` a single item or a string — never the array.**\n  `imageFrom([item])` returns `\"\"` (empty), not the URL. Use `value[0]`.\n- **Guard `undefined`/`null`.** `imageFrom(undefined)` / `imageFrom(null)`\n  **throw** (`Cannot read properties … (reading 'fields')`). The `photo?.value?.[0]\n  ? … : undefined` shape guards this; `imageFrom({}).url()` returns `\"\"`.\n\n`transform` options (`ImageFromTransformProps`): `width?`, `height?`,\n`fit?: 'scale-down' | 'contain' | 'cover'`; and for `fit: 'cover'` also\n`focal: 'auto' | 'center' | { x, y }` (numbers 0–1). The asset's own focal point\nis respected automatically. Bare `imageFrom(asset).url()` extracts the URL with no\ntransform.\n\n### With `next/image`\n\nEnable the Uniform image host, then feed `imageFrom(...).url()` as `src`:\n\n```ts\n// next.config.ts\nimages: { remotePatterns: [{ protocol: \"https\", hostname: \"img.uniform.global\" }] }\n```\n\nRead intrinsic dimensions from the raw item (`item.fields.width?.value`) for DAM\nassets; fall back to sensible defaults for external images.\n\n## 4. Read other fields — off the raw item\n\n`imageFrom` gives you the `src`. You already hold the **raw asset item** you passed\nto it (`photo?.value?.[0]`), so **read the other fields straight off that item** —\neach is a `{ type, value }` pair under `.fields`:\n\n```tsx\nconst item = photo?.value?.[0];\nconst alt = item?.fields.title?.value ?? item?.fields.description?.value ?? \"\";\nconst width = item?.fields.width?.value;\nconst height = item?.fields.height?.value;\n```\n\nAn item carries under `.fields` (whatever the asset has): `url`, `title`,\n`description`, `mediaType`, `width`, `height`, `size`, `focalPoint`.\n\n## 5. The stored asset-value shape\n\nWhat lives in the composition JSON (and what Canvas edits) is an **array of\nasset items** (`AssetParamValueItem[]`), even for a single image:\n\n```json\n\"photo\": {\n  \"type\": \"asset\",\n  \"value\": [\n    {\n      \"type\": \"image\",\n      \"_id\": \"<uuid>\",\n      \"_source\": \"custom-url\",\n      \"fields\": {\n        \"url\":       { \"type\": \"text\",   \"value\": \"https://…/photo.jpg\" },\n        \"title\":     { \"type\": \"text\",   \"value\": \"Jane Doe\" },\n        \"mediaType\": { \"type\": \"text\",   \"value\": \"image/jpeg\" },\n        \"width\":     { \"type\": \"number\", \"value\": 300 },\n        \"height\":    { \"type\": \"number\", \"value\": 300 }\n      }\n    }\n  ]\n}\n```\n\n`_id` is a unique ID that can be passed to a key attribute in certain frontend frameworks like React.\n\n`_source` identifies where the asset came from (constants from `@uniformdev/canvas`):\n\n- `ASSETS_SOURCE_UNIFORM` = `\"uniform-assets\"` — a managed **DAM library** asset.\n- `ASSETS_SOURCE_CUSTOM_URL` = `\"custom-url\"` — an **external URL** with\n  manually-set fields (no upload).\n\nAt render time `photo.value` arrives as this same `AssetParamValueItem[]` (each\nelement has `.fields`) — which is exactly what `imageFrom(value[0])` wants.\n\n## Reference\n\n- Rendering, `ComponentProps`, slots: `uniform-nextjs-app-router` skill,\n  `references/components.md` (§ \"Asset parameters\").\n- Uniform docs: \"Rendering assets\" — https://docs.uniform.app/docs/guides/composition/manage-assets/rendering-assets\n- Modeling parameters/slots well: `uniform-experience-modeling` skill.\n"
}

SHA-256 of public snapshot: 5012bac8265f541d4016d7397b07c3767cf0c95cae016581c812075619b2ca9a