Assets in Content
The asset library holds your images, videos and documents. Content pages use them as references: an author picks an asset in a Component, and delivery returns that asset’s details beside the rest of the page. The file is never copied into the page, so replacing a file in the library changes every page that uses it, with nothing republished.
What a reference looks like
Section titled “What a reference looks like”An asset reference is an expanded reference like any other, and its provider is the name you gave the
Assets registration in Content. What tells you the kind of asset is the Contract it points to:
contract.externalId |
Holds |
|---|---|
ebitex-image |
An image, with its pixel size |
ebitex-video |
A video, with its duration and a poster image |
ebitex-document |
A document such as a PDF |
A field that allows more than one kind delivers all of them through the same shape. Check
contract.externalId, or let the SDK’s isAssetValue do it:
// Renders a reference to an image, video or document picked from the asset library. A field can allow// several kinds, and every kind arrives through the same shape, so the check is on the value itself.import { isAssetValue } from '@ebitex/content-sdk'
export function Media({ value }: { value: unknown }) { // `isAssetValue` looks at the Contract the reference points to: ebitex-image, ebitex-video or // ebitex-document. A reference that cannot be resolved arrives without `content`, and is not an asset // value, so it renders nothing. if (!isAssetValue(value)) return null
const { content } = value switch (content.kind) { case 'image': // `file` is already the right file for the requested locale. Its width and height are the // pixel size of that file, so the browser can reserve the space before it loads. return <img src={content.file.url} width={content.file.width} height={content.file.height} alt={content.alt ?? content.name} /> case 'video': // `file.url` is a streaming manifest (HLS). Pass it to a player that plays HLS; the poster is a // plain image. return content.file.posterUrl ? <img src={content.file.posterUrl} alt={content.name} /> : null case 'document': return <a href={content.file.url}>{content.name}</a> }}The content of a reference holds these members:
| Member | Meaning |
|---|---|
name |
The asset’s name in the library |
kind |
image, video or document |
visibility |
public or private |
thumbnail-url |
A small preview image, when the asset has one, as an external link: { "kind": "external", "url": "…" } |
file |
The file itself: url, contentType and sizeBytes, plus width and height for an image, or durationSeconds and posterUrl for a video |
alt |
Alternative text, for images |
caption |
A caption |
An image’s file starts with the same three members as a Blob (url, contentType, sizeBytes), so
code that reads a Blob reads an asset’s file unchanged.
Rendering an image that may be an asset or a Blob
Section titled “Rendering an image that may be an asset or a Blob”A field that allows both a Blob image Contract and the asset library’s ebitex-image Contract delivers
two shapes. They agree on file.url, file.contentType and file.sizeBytes, and differ in two places:
| Blob image | Asset image | |
|---|---|---|
| Alternative text | alt-text |
alt |
| Width and height | top level, width and height |
inside file, file.width and file.height |
Switch on contract.externalId, and treat a missing dimension as absent rather than as 0:
import { isAssetValue } from '@ebitex/content-sdk'
function readImage(value) { if (isAssetValue(value)) { if (value.contract.externalId !== 'ebitex-image') return null // a video or document const { file, alt } = value.content return { url: file.url, alt: alt ?? '', width: file.width, height: file.height } } const { file, width, height, 'alt-text': alt } = value.content return { url: file.url, alt: alt ?? '', width, height }}A video’s file.url is an HLS manifest. Safari and iOS play it in a plain <video src>; other browsers
need a player such as hls.js. A document’s file.url is a direct link, so show a download link with the
name, the extension of the URL and file.sizeBytes.
Pass width and height to the <img> when you have them, so the browser can reserve the space
before the file loads.
One file per locale
Section titled “One file per locale”An asset has a default file and, optionally, a file for any locale in your organization’s locale tree.
alt and caption can differ per locale too. Delivery chooses one for the locale you ask for, along
the locale tree: a request for de-AT gets the de-AT file if there is one, then the de file, then
the default. It never matches by the look of the code, so de-CH gets the default unless your tree
makes it a child of de. See Localization for the tree.
file is chosen as a whole: its URL and its dimensions always come from the same rendition.
With raw=true you receive the asset as all of its renditions instead: file becomes an object with a
default and a locales map. An author can also set alt and caption for one placement, and a
placement’s value wins over the asset’s own for that page.
Public and private assets
Section titled “Public and private assets”A public asset’s URL is stable and open. Put it straight in an <img>.
A private asset is reachable only through a key whose channel allows private assets, and its URLs are signed: they stop working after one hour. Do not store them and do not cache a response that holds one for longer than a page view. A private asset can be made public in the library, and that is a one-way change. Revoking a public asset means deleting it.
Your own fields on an asset
Section titled “Your own fields on an asset”An organization can add its own fields to each kind of asset: a photographer, a licence, an expiry date. A field
is either delivered or internal. Internal fields never leave the asset library. Delivered fields are
returned on every channel, as members of the asset’s document beside name and file, named by their keys:
{ "name": "Harbour at dawn", "photographer": "Maya Ortiz", "licence": { "provider": "core", "key": "9d4c…" }, "source-url": { "kind": "external", "url": "https://harbour.example/press" }}A field the asset has no value for is left out. A link is always { "kind": "external", "url": … }, a category is
a reference to the organization’s shared vocabulary, a date is a UTC instant, and a number or a yes/no is the plain
value. GET /assets/delivery/v1/contracts describes every delivered field, so a client can read the exact shape
instead of guessing. Content reads the same description, so a delivered field appears on an asset picked in a
Component after Content’s next sync.
Every asset always matches its kind’s fields. Creating or editing an asset through the management API without a
value for a required field is refused with a 400, whether the asset is private or public.
Channels decide what a key sees
Section titled “Channels decide what a key sees”Assets are not read with a Content delivery key. In the library, a channel names the folders and categories a consumer may see and whether private assets are included, and each key you mint belongs to one channel. You register the Assets provider in Content with a channel’s key, so Content sees what that channel allows, and an asset outside the channel behaves exactly as if it did not exist. Use one channel per site or audience.
When a change shows up
Section titled “When a change shows up”Content reads the asset when a page is delivered, so a rename, a new rendition or a visibility change reaches your pages without a republish. It is not instant: the Assets registration keeps what it read for a few minutes (the registration’s cache time, 5 minutes by default), and delivery responses are cached for a short time as well. Plan for a change to appear within about ten minutes.
Delete an asset and every reference to it is delivered as unresolvable, the same descriptor any
missing reference becomes. Render nothing for it, and have an author pick another asset.
Reading assets directly
Section titled “Reading assets directly”The Assets Delivery API serves the same assets to your own code with a channel key. Its responses are never cached, because they can contain signed links.