Image

ImageRenderable displays PNG, JPEG, WebP, or GIF data from an encoded source. It also accepts an existing NativeImage. The renderable uses Kitty graphics, Sixel, or a Unicode block fallback.

Use NativeImage when you need to decode, inspect, or transform image pixels before display.

Availability#

Field Availability
Package @opentui/core
Core renderable ImageRenderable
React <image> (automatic)
Solid <image> (automatic)
Status Built in

Renderable API#

import { ImageRenderable, createCliRenderer } from "@opentui/core"

const renderer = await createCliRenderer()
const image = new ImageRenderable(renderer, {
  id: "cover",
  source: "./cover.webp",
  width: 40,
  height: 15,
  fit: "cover",
  protocol: "auto",
  onError: console.error,
})

renderer.root.add(image)
await image.loadPromise

source accepts every ImageSource form: a path, file:/HTTP(S)/blob:/data: URL, URL, Blob, Response, Uint8Array, or ArrayBuffer. It also accepts NativeImage. Format detection uses encoded bytes when the source needs decoding. ImageRenderable retains an existing NativeImage. You still own and must dispose the source reference.

React#

import { createCliRenderer } from "@opentui/core"
import { createRoot } from "@opentui/react"

const renderer = await createCliRenderer()
createRoot(renderer).render(
  <image source="./cover.webp" fit="cover" protocol="auto" style={{ width: 40, height: 15 }} />,
)

Solid#

import { createCliRenderer } from "@opentui/core"
import { render } from "@opentui/solid"

const renderer = await createCliRenderer()
await render(
  () => <image source="./cover.webp" fit="cover" protocol="auto" style={{ width: 40, height: 15 }} />,
  renderer,
)

You can update source, callbacks, fit, and protocol after construction. Replacing source keeps the current image visible until the replacement succeeds. It cancels obsolete loading and disposes stale native images. Setting source to undefined clears it. Clearing fit or protocol restores "fit" or "auto".

Sizing#

fit Behavior
fit Contain and center the full image. Preserve aspect (default)
cover Fill the renderable, preserve aspect ratio, and center-crop
fill Fill the renderable and allow stretching

Sizing uses terminal pixel resolution when available and a 2:1 cell-height fallback otherwise. During startup and resize, Sixel images temporarily use blocks until current pixel geometry arrives.

Rendering protocol#

protocol Behavior
auto Global override, then Kitty, then Sixel, then Unicode blocks
kitty Force Kitty graphics
sixel Force Sixel. Falls back to blocks without terminal pixel resolution
blocks Portable Unicode quadrant-block rendering

With global and per-image protocols set to auto, tmux uses blocks. Explicit Kitty, or Sixel with pixel resolution, uses tmux passthrough.

Overlapping images must use the same effective protocol. OpenTUI does not support layering or alpha composition across different effective protocols. Leave overlapping images on auto or give them the same explicit protocol. Non-overlapping images can use different protocols.

Kitty preserves image alpha, Sixel treats alpha below 128 as transparent, and blocks blend sampled alpha. Placement opacity scales Kitty and block alpha. Sixel dims toward cell backgrounds. Direct, unbuffered fills, text, and box borders cover images at whole-cell granularity without blending.

Use OPENTUI_IMAGE_PROTOCOL=auto|kitty|sixel|blocks to set the global default. OPENTUI_GRAPHICS=false disables Kitty and Sixel detection. See Environment variables.

Split-footer scrollback snapshots use the same protocol resolution as live images. They use Kitty placement, Sixel with detected pixel geometry, or Unicode quadrant blocks. Snapshots with mixed effective protocols, overlapping images, or covered Sixel cells use blocks. Native scrollback placement starts when the footer is pinned and the image rectangle is addressable. Await loadPromise before rendering an image into a ScrollbackSurface. See Writing to scrollback.

resolveImageRenderProtocol(requested, capabilities, hasResolution) exposes the same protocol-resolution policy for code that needs it without constructing a renderable.

Options and state#

Member Type Description
source ImageRenderableSource Encoded image source or NativeImage. Optional
fit "fit" | "cover" | "fill" Destination sizing
protocol "auto" | "kitty" | "sixel" | "blocks" Requested rendering protocol
onLoad (image: NativeImage) => void Called after the current source loads
onError (error: unknown) => void Called when the current source fails
image NativeImage | null Currently displayed renderable-owned image
loading boolean Whether the current source is loading
loadError unknown Current load error, otherwise null
loadPromise Promise<void> | null Settles after the current load attempt
effectiveProtocol kitty | sixel | blocks Current resolved protocol. Can change with capabilities or size
cellAspectRatio number Physical or fallback cell aspect ratio
getFittedSize(...) (width, height, cellAspect?, sourceWidth?, sourceHeight?) Resolve destination cells. Omitted overrides use current values

The renderable owns image and the NativeImage passed to onLoad. Do not dispose or transfer them. Current-source failures set loadError, call onError, and resolve loadPromise. Superseded, cleared, or destroyed loads resolve without callbacks. Exceptions from either callback reject after state settles. Successful replacement, clearing, and destruction release owned images. A failed replacement keeps the current image.

Use FrameBuffer when you need direct cell drawing or OptimizedBuffer.drawImage(). Use QR code for text encoded as a scannable symbol.