Osy#betaa language · its runtime Osyrin · a hosted platform
Why Osy#Built for agentsAgents as declarationsWorkflows that waitRuns exactly onceSecure by defaultNothing to mockThe editor is the compilerUI in the languageDocuments are dataOne program

Reference / UI

Showing a picture on a page

Image(src: "/logo.png", alt: "…") · Image(fileAsset: item.Photo.Id, alt: "…")

`Image` is the element that shows a picture, and it addresses one of two ways. `src` takes a URL — for a file in the app's own path store, `File.Url(path)` builds it. `fileAsset` takes the id of a stored `FileAsset` row, which has no path at all: the renderer asks the platform for a short-lived address when it draws, so a private photograph is shown without the app minting or handling a URL. Exactly one of the two, always.

stable3 examples compiled by CIuiatomsimagesstorage

Summary#

Image is a renderer primitive — no using, nothing to install. It renders one picture, and the argument you give it says where the picture is:

you havewritewhat it is
a URL, or a path in the app's own file storeImage(src: File.Url(item.ImagePath))a plain address the browser fetches
a FileAsset row — what File.Create and Image.Thumbnail answer withImage(fileAsset: item.Photo.Id)the platform works out the address

Give it one or the other, never both and never neither — an Image shows one picture, and an element with no address draws a broken-image glyph rather than nothing, which reads as a failure of the app.

Signature#

Image("/logo.png")                                     // the first positional IS `src`
Image(src: File.Url(item.ImagePath), alt: "A wheel")   // a file in the app's own PATH store
Image(fileAsset: item.Photo.Id, alt: item.Photo.AltText)   // a stored FileAsset ROW

Everything else an Image takes is the ambient vocabulary every element has — the style props (w, h, rounded, objectFit), the accessibility props, the event props. osy kit atoms Image prints the lot.

Description#

Why is there a second way at all?#

Because the platform stores files two ways, and only one of them has a path.

A path-keyed file lives at an app-relative address you chose — public/logo.png — and File.Url turns that into a URL. That is what the upload control produces, and src is how you show it.

A FileAsset is a row. It has an id, a name, a MIME type and its own security, and it has no path — which is why File.Url/File.SignedUrl, whose argument is a path, cannot address one. It is what File.Create answers with, what Image.Thumbnail/Resize/Convert answer with (see Image.Thumbnail, Resize and Convert (a stored image, transformed)), and what a FileAsset field on your entity holds. fileAsset: is how you show one.

Why the row, and not a URL#

The address a browser can fetch a non-public FileAsset from is a short-lived signed grant, minted for one caller and expiring in minutes — see File.SignedUrl for the same idea over a path. So it is not a value an app can compute while a page is being drawn, and it is not something you would want to hold: it would go stale under a reader who left the page open.

So the element carries the row, and the renderer asks for an address when it actually needs one, renews it before it expires, and asks once however many places on the page show the same file. The access decision is your entity's own security {} block and nothing else: a reader who may not read the asset is refused the address.

What a reader sees when they may not see it#

A refused image shows nothing, and says so in its alternative text, rather than the browser's broken-image icon. That distinction is deliberate: "you may not see this" and "this app is broken" look identical on screen and are different facts. Write an alt and the refusal is appended to it.

Showing a photograph somebody uploaded#

The whole loop — a row that holds a picture, and a page that shows it:

using Osysharp.Storage;
using Osysharp.Ui;

entity Listing {
  [Required, MaxLength(140)] string Title;
  FileAsset? Photo;
  security { allow read, create, update when IsAuthenticated || IsAnonymous; }
}

[Page("/listings")]
[AllowAnonymous]
component Listings() {
  live var rows = Listing.OrderBy(l => l.Title);
  render {
    Stack(gap: 4, p: 8) {
      foreach (var l in rows) {
        Row(gap: 3, align: Align.Center) {
          Image(fileAsset: l.Photo.Id, alt: l.Title, w: 120, rounded: Radius.Md);
          Text(l.Title);
        }
      }
    }
  }
}

l.Photo is the row and l.Photo.Id is what the element takes — the same spelling PdfViewer and PdfThumbnail take, so every stored file is addressed the same way.

A thumbnail rather than the full picture#

A list of twenty photographs should not send twenty full-size images to a phone. Image.Thumbnail makes a smaller variant, stores it, and answers a FileAsset of its own — which is another thing to show:

using Osysharp.Images;

entity ListingThumb {
  [Required] Listing Of;
  FileAsset? Small;
  security { allow read, create, update when IsAuthenticated || IsAnonymous; }
}

ListingThumb MakeThumb(Listing listing) {
  var thumb = new ListingThumb { Of = listing, Small = Image.Thumbnail(listing.Photo, 128) };
  return thumb;
}

Nothing is generated on upload: the variant exists the moment you ask for it, is deduplicated by content, and is addressed by Image(fileAsset: t.Small.Id) exactly like the original.

A picture in the app's own file store#

When the file is one the app put somewhere by path — a logo, a seeded asset, an upload result — there is no row and no signing. File.Url builds the address and src takes it:

using Osysharp.Storage;

entity Brand {
  [Required, MaxLength(200)] string LogoPath;
  security { allow read when IsAuthenticated || IsAnonymous; }
}

string LogoUrl(Brand brand) { return File.Url(brand.LogoPath); }

See also#

Related

upload

`Upload(onUploaded: Ingest) { … }` is a file picker wearing whatever you put inside it. When someone chooses a file its…

camera and microphone

Take a photograph, record a video, or record audio. `Camera.Start()` asks for the device and `Camera()` shows the…

Files (addressing something the app stores)

The platform stores a file two ways, and which one you hold decides how you address it. A PATH-keyed file lives at an…

Image.Thumbnail, Resize and Convert (a stored image, transformed)

Three server-side transforms over a stored image, each answering a NEW `FileAsset`: `Thumbnail` fits the image within…

File.Url

Turns an app-relative file path into the public URL a browser can fetch it from — `File.Url("public/x.png")` returns…

File.SignedUrl

Mints a temporary, signed URL that lets an authorized browser fetch a PRIVATE app file — one stored outside `public/` —…

Osysharp.Ui (the UI kit)

The bundled UI kit — ready-made styled controls like `Button`, the shared design-system vocabularies (`Tone`, `Size`)…