Osy#betaRun it on Osyrin: osyrin.com

Reference / Config

the app's own icon (app.Icon)

app.Icon = Art.Logo; · head { Icon = Art.Kettle; }

`app.Icon` names one of the SVG files your app ships as its icon. The app serves it at its own `/favicon.svg`, and every page's head links it, so browser tabs, bookmarks and home-screen icons show your mark instead of the platform's. PNG and ICO versions are rendered from the same SVG for Google Search and phone home screens. A page can carry its own icon with `head { Icon = … }`.

preview1 example compiled by CIconfiguiseoicon

Summary#

Every page shows an icon in its browser tab. Until an app says otherwise, that is the platform's. One line makes it the app's own:

app.Icon = Art.Logo;      // art/logo.svg — one of the SVG files this app ships

Signature#

app.Icon = Art.<Name>;           // the app's icon — any page without its own
head { Icon = Art.<Name>; }      // one page's own icon, on that page only

Art is the vocabulary of SVG files the app ships (SVG assets). The icon is named from it rather than written as a path, so a missing file is a compile error that lists the names that exist. Anything other than an Art.X is refused with the one-line fix.

Description#

Where it is served, and who reads it#

The icon is served at the app's own /favicon.svg, on the app's own host (its subdomain), and every page's head links it. The address never changes between versions. A new icon is picked up by revalidation, not by a new URL.

Several things read an icon, and each reads it from the origin the page is on:

  • a browser tab and the history list;
  • a bookmark;
  • a home-screen icon, when someone adds the app to a phone's home screen.

So every app on its own subdomain shows its own icon in all three.

Google Search follows its own rules, published in Google Search Central's favicon guidelines (https://developers.google.com/search/docs/appearance/favicon-in-search, last updated 2026-08-28, read 2026-09-28):

  • "Google Search only supports one favicon per site, where a site is defined by the hostname." The page lists a subdomain home page (https://news.example.com) as supported, and a subdirectory (https://example.com/news) as not supported. So an app on its own subdomain can have its own icon in Search. It does not inherit the root domain's.
  • Google finds the icon through the <link rel="icon"> on the home page. Googlebot must be able to crawl that page, and Googlebot-Image must be able to crawl the icon. An app's / page is therefore what decides its Search icon.
  • The listed formats are "BMP, GIF, ICO, PNG, JPEG, PPM, and TIFF", and the page recommends larger than 48×48 px. SVG is not on that list, so Search reads the raster versions the platform renders for you (below).

The raster versions#

The same SVG is rendered to the formats that cannot read an SVG, once per app version, all on the app's own host:

addresssizeread by
/favicon.ico16, 32 and 48 px, each a PNG inside the ICOa browser that asks without a link, and Google Search
/favicon-48.png · /favicon-32.png · /favicon-16.pngas namedGoogle Search (48 px or larger is what it recommends), older browsers
/apple-touch-icon.png180 pxa phone's home screen (iOS)

Every page's head links them beside the SVG, so a tab, a bookmark, a home screen and a search result all show the same mark. The icon is fitted into the square and centred on transparency, so an SVG that is not square keeps its proportions. An app with no app.Icon gets the platform's icon at every one of these addresses.

A page's own icon#

head { Icon = Art.Kettle; } gives one page its own icon. That page links /favicon.svg?icon=kettle, which serves that asset. After a client navigation, the tab's icon follows the page and returns to the app's icon on a page without its own. ?icon= serves only the app's own art, which is public chrome the app already ships.

Examples#

An app with its own icon, and one page with another#

app.Icon = Art.LogoFull;                   // art/logo_full.svg

[Route("/credits")]
[AllowAnonymous]
[Title("Credits")]
component Credits() { render { Text("Made with Osy#"); } }

[Route("/credits/board")]
[AllowAnonymous]
[Title("The board")]
component CreditsBoard() {
  head { Icon = Art.Hexgrid; }             // art/hexgrid.svg, on this page only
  render { Text("The board"); }
}

What the compiler refuses#

app.Icon = "/logo.svg";     // `app.Icon` is one of the SVG files this app ships — `app.Icon = Art.Logo;`
app.Icon = Art.Logoo;       // unknown member — the message lists the Art the app has

See also#

Related

head

A routed page's document head — its title, description, canonical address, share image, structured data, whether it may…

SVG assets

Drop a `.svg` into `model/art/` and render it with `Svg(Art.Hexgrid)`. Unlike an icon, an asset keeps its own colours…

moved addresses (app.Redirects)

`app.Redirects` lists the addresses your app has moved and where they went. A request for one is answered with a 301…