Skip to content

Image

A picture that holds its space while it loads, shows that it is loading, and shows something useful when it fails. A bare <img> leaves all three to the caller.

tsx
import { Image } from 'neba';

<Image src={src} alt="Terraced tea fields under morning mist" ratio="16 / 9" rounded />;

Props

PropTypeDefaultDescription
srcstringWhere the picture is
altstringRequired, by the type. A missing alt and an empty one mean different things("nobody wrote this" and "this says nothing a reader needs") and only the second is ever correct, so you are made to say which
rationumber | string | 'auto''auto'The proportion to hold while the file is arriving: the main reason to use this: an unreserved picture pushes the page down when it lands
width · heightnumber | stringThe file's own pixel dimensions, passed to the img as they are. Give both and an auto ratio becomes their proportion, so nobody has to work out that 1200 by 800 is 3/2
fit'cover' | 'contain' | 'fill' | 'none''cover'How the picture fills that box
roundedNebaSize | booleanfalseRounds the corners at this step of the radius ladder. true is md
placeholderReactNode | falseWhat stands in while the file is arriving. A Skeleton of the right shape by default
fallbackReactNodeDrawn instead when it does not arrive. A box carrying the alt by default: the browser's own torn-page glyph says the site is broken rather than one file
previewbooleanfalseOpens the full picture in a Dialog on click. The picture becomes a button named by its alt, so a keyboard can open it too
filter'none' | 'grayscale' | 'sepia' | 'invert' | 'saturate' | 'mute' | 'contrast' | string'none'How the picture is coloured: one of seven names, or a CSS filter chain of your own. It travels, so changing it on hover fades rather than snaps
frame'rect' | 'rounded' | 'circle' | 'cut' | 'arch' | NebaImageFrameOptionsHow the picture is mounted: the silhouette it is cut to, and the line, the mat, the shadow and the softened edge around it
watermarkstring | NebaImageWatermarkOptionsA mark drawn over the picture: a credit, a licence, the word DRAFT. repeat tiles it. A deterrent, not a lock
protectboolean | { contextMenu?: boolean; drag?: boolean; select?: boolean }falseTurns off the ways a picture is casually taken: the right-click menu, the drag, the iOS long press, the selection. The file itself is still one request away
onLoadingStatusChange(status: 'loading' | 'loaded' | 'failed') => voidCalled as the status changes. A new src starts over at loading
localestringThe language of the one sentence used when the file fails and alt is empty. A BCP 47 tag
unavailableLabelstringWrites that sentence out instead. Defaults to the locale's wording
classNamesNebaSlots<'image' | 'placeholder' | 'fallback' | 'frame' | 'watermark'>Class names for the parts behind the root. The root itself is className, so there is no root key

Native <img> attributes pass through to the picture itself: loading, decoding, srcSet, sizes, referrerPolicy, and width and height among them.

alt is required

By the type, which is the one place this is stricter than the tag it wraps.

A missing alt and an empty one mean different things ("nobody wrote this" and "this picture says nothing a reader needs"), and only the second is ever correct. Being made to type alt="" is being made to say which one you meant.

Examples

ratio

The proportion to hold while the file is still arriving, and the main reason to use this over an <img>. A picture with no reserved box pushes the page down when it lands, which is the single largest source of layout shift on most sites.

tsx
<Image src={src} alt="…" ratio="16 / 9" />
<Image src={src} alt="…" ratio={1} />

'auto' is the default and lets the file decide, which reserves nothing on its own.

width and height

The file's own pixel dimensions, as an <img> takes them. They reach the picture either way, and giving both turns an 'auto' ratio into their proportion, so the box is reserved without anybody working out that 1200 by 800 is 3/2.

tsx
<Image src={src} alt="…" width={1200} height={800} />

ratio is the layout's shape and these two are the picture's, so an explicit ratio outranks them. One on its own reserves nothing, since a proportion needs two numbers.

fit and rounded

fit is object-fit: cover (the default), contain, fill, none. rounded takes a step of the radius ladder, or true for md.

placeholder and fallback

While the file is arriving, a Skeleton of the same shape stands in. Pass a node of your own, or false for nothing.

When it does not arrive, fallback is drawn instead: by default a box carrying the alt text. Something rather than nothing, because the browser's own torn-page glyph tells a reader the site is broken rather than that one file is missing.

Changing src starts both over. Without that, a second file would inherit the first one's success and never show a placeholder, and a second file that failed would inherit it too.

preview

Opens the full picture in a Dialog when it is clicked.

The picture becomes a <button> carrying the alt as its name, so Tab reaches it and Enter opens it. An image only a pointer can enlarge is an image half the readers cannot enlarge.

filter

How the picture is coloured. Seven names (grayscale, sepia, invert, saturate, mute, contrast, and none, the default), or a CSS filter chain of your own for anything past them.

The treatment travels on the same clock as the picture's own fade, so a className that changes it under the pointer is a thumbnail that comes back to life rather than one that snaps.

frame

How the picture is mounted. A silhouette on its own (frame="circle"), or the whole arrangement written out: shape, corner, border, borderColor, mat, background, elevation and feather.

The line is drawn as an inset shadow rather than a border, which is what lets it follow a cut corner or a circle and what keeps it out of the layout. mat is the one part that takes room: it is the mount between the line and the picture.

watermark

A mark drawn over the picture. A string is placed once in the bottom corner; the options form takes content, position, repeat, opacity, rotate, size and color.

repeat tiles the mark across the whole picture, which is the arrangement that actually deters a screenshot. It needs text: a node cannot be drawn into the tile, and is placed once instead.

protect

Turns off the ways a picture is casually taken: the right-click menu, the drag that drops a copy into another window, the iOS long press, and the selection a Ctrl-A sweeps up. protect turns on all four; the options form takes contextMenu, drag and select separately.

A deterrent and not a lock. The file is still one request away in the network tab, and a reader who wants it will have it: what this stops is the copy that gets made without thinking about it. Turning it on to protect a secret is turning it on for the wrong reason.

onLoadingStatusChange

Called with 'loading', 'loaded' or 'failed'. Useful for swapping to a src you control, or for counting what did not arrive.

Accessibility

  • alt is the picture's accessible name. Write what the picture says, not what it is a picture of, and use alt="" when it says nothing the surrounding text does not.
  • With preview, the button takes its name from alt and nothing else. Two names for one thing is a screen reader reading the same sentence twice.
  • The placeholder and the fallback are not announced separately; the picture keeps its own name throughout.
  • A failed picture with an empty alt has no name to fall back on, so the box says so in the page's own language. Set locale, or write the sentence out with unavailableLabel.
  • A watermark is aria-hidden and takes no pointer events. What it says belongs in the text around the picture, or in the alt, where a reader who cannot see the mark still meets it.
  • protect takes away a browser affordance rather than adding one. Nothing it turns off is a keyboard path or a screen reader path, but "open image in new tab" goes with the context menu, so turn it on where the mark on the picture is the point, and not by default.

Released under the MIT License