Img Component & Lazy Loading

Use the built-in <Img> component to keep images from blocking initial render. It ships a real <img> tag on the server and lazily swaps in the real source once it nears the viewport — no 'use client' needed in your own file.

Core Component

Overview

Use the built-in <Img> component for images that shouldn't block initial render. It's a "use client" boundary internally, but you can drop it into a server component like any other tag — no "use client" directive needed in your own file.

app/pages/gallery.tsxtypescript
import { Img } from 'nukejs'

export default function Gallery() {
    return (
        <Img
            src="/photos/mountain.jpg"
            alt="Mountain at sunrise"
            width={800}
            height={450}
        />
    )
}
ℹ️
No layout shift On the server, <Img> renders a real <img> tag immediately — width/height reserve the space so there's no jump when the real image loads, and nothing needs to hydrate in later that could flash missing content.

How it works

In the browser, the component doesn't set src to the real image right away. Instead it watches itself with IntersectionObserver and only swaps in the real src once the element is about to enter the viewport (controlled by rootMargin, 200px by default). Until then it shows placeholder if you gave it one, or just waits.

Browsers that don't support IntersectionObserver, and the initial server render, always get the real image — the component never hides content it can't guarantee it'll lazily reveal later.

Blurred placeholders

Pass a small/blurred placeholder image and <Img> will show it (with a CSS blur) until the real image finishes loading, then cross-fade:

app/pages/gallery.tsxtypescript
<Img
    src="/photos/mountain-full.jpg"
    placeholder="/photos/mountain-tiny.jpg"
    alt="Mountain at sunrise"
    width={800}
    height={450}
/>

Loading images eagerly

Lazy loading is wasted (and can even hurt LCP) on above-the-fold images like a hero banner. Set eager to skip the IntersectionObserver step and load immediately:

app/pages/home.tsxtypescript
<Img src="/hero.jpg" alt="Welcome" width={1600} height={600} eager />

Props

PropTypeDescription
srcstringImage URL — swapped in once the image is near the viewport
altstringAlt text (required)
widthnumber | stringRendered width — set alongside height to prevent layout shift
heightnumber | stringRendered height
placeholderstringLow-res/blurred image shown until src loads
rootMarginstringHow far ahead of the viewport to start loading — passed straight to IntersectionObserver. Default "200px"
eagerbooleanSkip lazy-loading and load src immediately. Use for above-the-fold images
classNamestringCSS class(es) applied to the underlying <img>
styleReact.CSSPropertiesInline styles merged with the blur transition NukeJS applies internally
onLoad(e) => voidFired when the real image finishes loading
onError(e) => voidFired if the image fails to load

Sample

See a runnable example with a full gallery, blurred placeholder, and eager hero image on the Img sample page.