# Showing a picture on a page

> `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.

<!-- id: ui-image · area: ui · stability: stable · html: https://osysharp.com/reference/ui/image/ -->

## Summary        {#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 have | write | what it is |
|---|---|---|
| a URL, or a path in the app's own file store | `Image(src: File.Url(item.ImagePath))` | a plain address the browser fetches |
| a `FileAsset` row — what `File.Create` and `Image.Thumbnail` answer with | `Image(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      {#signature}
```osy syntax
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    {#description}

### Why is there a second way at all?   {#two-addresses}
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](https://osysharp.com/reference/storage/file-url/) turns
that into a URL. That is what the [upload](https://osysharp.com/reference/ui/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)](https://osysharp.com/reference/storage/images/)), and what a `FileAsset` field on
your entity holds. `fileAsset:` is how you show one.

### Why the row, and not a URL   {#why-the-row}
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](https://osysharp.com/reference/storage/file-signed-url/) 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   {#refused}
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   {#example}
The whole loop — a row that holds a picture, and a page that shows it:

```osy title="an entity with a photograph, and the page that shows it" test app=ui-image
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   {#thumbnail}
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:

```osy title="store a small variant once, then show that instead" test app=ui-image
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   {#path-store}
When the file is one the app put somewhere by path — a logo, a seeded asset, an [upload](https://osysharp.com/reference/ui/upload/) result — there is no
row and no signing. `File.Url` builds the address and `src` takes it:

```osy title="a path-keyed file: File.Url builds the address, src takes it" test app=ui-image
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       {#see-also}
- [Files (addressing something the app stores)](https://osysharp.com/reference/storage/index/) — the two ways a file is stored, and which address each one has
- [Image.Thumbnail, Resize and Convert (a stored image, transformed)](https://osysharp.com/reference/storage/images/) — `Image.Thumbnail`, `Resize` and `Convert`: a stored image transformed into another one
- [File.Url](https://osysharp.com/reference/storage/file-url/) — the public address of a PATH-keyed file, which is what `src` wants
- [File.SignedUrl](https://osysharp.com/reference/storage/file-signed-url/) — a time-limited grant to one caller, over a path
- [upload](https://osysharp.com/reference/ui/upload/) — where a picture usually comes from
- [camera and microphone](https://osysharp.com/reference/ui/capture/) — a photograph from the camera, arriving as the same `UploadedFile`
