Elements

Image

<Image>, imported from nsh/image, shows a picture from the app’s assets or from a URL. Images are downloaded and decoded off the main thread, downsampled to the size the view shows them at, and cached in memory. Animated GIF, WebP and APNG images play.

import { View } from "nsh";
import { Image } from "nsh/image";
import logo from "./assets/logo.png";

<View style={{ flexDirection: "row", gap: 12 }}>
  <Image src={logo} />
  <Image
    src="https://example.com/avatar.jpg"
    defaultSource={logo}
    resizeMode="cover"
    style={{ width: 64, height: 64, borderRadius: 32 }}
  />
</View>;

An imported asset knows its size: without a width or height the image takes it, and with only one of them the other follows the asset’s aspect ratio. A URL has no known size before it loads, so give remote images a width and a height (or an aspectRatio with one of them).

#Sources

src (and defaultSource) takes:

ValueExampleDescription
An imported assetimport logo from "./logo.png"An ImageAsset { uri, width, height }, sized in points: a @2x file counts half its pixels. .png, .jpg, .jpeg, .gif, .webp and .heic can be imported.
A URL"https://example.com/a.jpg"Downloaded and cached. Downloads of the same URL are shared between views.
An asset path"asset:/images/a.png"A file of the app’s assets.
{ uri }{ uri: "https://…" }The same as the string.

During development assets are served by the dev server; nsh build and nsh package copy them into the app. See images and assets.

#Props

Besides the common props (with style typed as ImageStyle):

PropTypeDefaultDescription
srcstring | ImageAsset | { uri: string }The image.
resizeMode"cover" | "contain" | "stretch" | "center""cover"How the image fits its box.
defaultSourcestring | ImageAsset | { uri: string }Shown until src has loaded; best a local asset.
blurRadiusnumberBlurs the image by this radius in points (applied once while decoding, off the main thread).
fadeDurationnumberiOS 0, Android 300How long a loaded image fades in, in milliseconds.
onLoadStart() => voidLoading began.
onProgress(e: { loaded: number; total: number }) => voidDownload progress in bytes (total is 0 when unknown), at most every 100 ms.
onLoad(e: { width: number; height: number }) => voidLoaded; the source image’s size in pixels (not the decoded, downsampled one).
onError(e: { message: string }) => voidLoading failed.
onLoadEnd() => voidAfter onLoad or onError.

ImageStyle adds tintColor to the view styles: it recolors a template image (an icon drawn in one color).

<Image src={icon} style={{ width: 24, height: 24, tintColor: "#0969da" }} />

#Events

EventPayloadWhen
onLoadStartnoneLoading began.
onProgress{ loaded, total }Bytes arrived (at most every 100 ms).
onLoad{ width, height }The image is shown; its source size in pixels.
onError{ message }Loading or decoding failed, or the host is not allowed.
onLoadEndnoneAfter onLoad or onError.
const state = signal("loading");
<Image
  src={url}
  style={{ width: "100%", aspectRatio: 16 / 9 }}
  onLoad={(e) => state.set(`${e.width}×${e.height} px`)}
  onError={(e) => state.set(e.message)}
/>;

To know an image’s size or load it before showing it, call Image.getSize(uri) and Image.prefetch(uri): the same Image from nsh/image that renders the element carries them.

#Ref methods

None of its own.

#Platform notes

  • The cache key includes the decode size and the blur radius: the same URL shown at two sizes is decoded twice but downloaded once.
  • iOS plays animated images as a keyframe animation (up to 600 frames); a tinted animated image shows its first frame.
  • Android plays animated images from API 28 (AnimatedImageDrawable); animated images are not cached there and are not blurred.
  • On Android borders are drawn above the image, as on iOS.