Osy#betaRun it on Osyrin: osyrin.com

Reference / UI

Showing a picture on a page

Image(src: "/logo.png", alt: "…") · Image(fileAsset: item.Photo.Id, alt: "…") · Image(texture: Textures.Harbour, 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
a picture the APP SHIPS, in model/textures/Image(texture: Textures.Harbour)compile-checked, served from the app's own origin

Give it exactly one of the three, never two and never none — 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
Image(texture: Textures.Harbour, alt: "The harbour at dusk")   // a picture the app ships in model/textures/

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; }
}

[Route("/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;
using Osysharp.Storage;

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); }

A picture the app ships — texture:#

The hero on a landing page, a category banner, a catalogue's product shots: imagery that is part of the APP rather than data somebody uploaded. Put the file in model/textures/ (.jpg, .png or .webp) and it becomes a member of the app's Textures enum — harbour_at_dusk.jpg is Textures.HarbourAtDusk — which texture: takes. A file name is letters, digits and underscores (a downloaded harbour-at-dusk.jpg is refused with the name to rename it to):

entity Print {
  [Required, MaxLength(120)] string Title;
  Textures Photo;                                  // which shipped picture this row shows — data, like any enum
}

Image(texture: Textures.HarbourAtDusk, alt: "The harbour at dusk", w: "100%", aspectRatio: "4 / 5", objectFit: ObjectFit.Cover)
Image(texture: print.Photo, alt: print.Title)

The name is checked at compile time, so a picture that is not shipped is an error rather than an empty box. The renderer sets the picture's address and its intrinsic width and height, so the page reserves the box and does not jump as pictures arrive. The bytes are content-addressed and served from the app's own origin — no third party sees a visitor, and a picture never disappears because someone else's link died.

Reach for texture: for imagery the app is BUILT with; for pictures people add while it runs, a FileAsset (fileAsset:) is the answer, and osy import carries a row's photo file with it (Importing data).

See also#

Related

textures

Drop `.png`, `.jpg` or `.webp` files into `model/textures/` and blit them onto a canvas with `Draw.Image(wall, …)`. The…

Playing a video on a page

`Video` plays one film, addressed one of three ways: a film the app SHIPS in `model/videos/` (`video: Videos.Harbour`…

Designing an app that looks designed — direction, type, colour, composition and motion

How to make an Osy# app look like somebody designed it rather than like the default: commit to one visual direction…

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`)…