@blurwind/next
v0.4.0
Published
Next.js <Image> adapter for blurwind — instant blur placeholders with a fade-in.
Maintainers
Readme
@blurwind/next
A genuine drop-in <Image> for Next.js with automatic blurwind
blur placeholders — native next/image blur by default (every prop passes
through), with an optional motion cross-fade.
Install
pnpm add @blurwind/next
pnpm add -D @blurwind/clinext, react, and motion are peer dependencies (you already have the first
two in a Next app).
Setup
1. Generate a manifest with the CLI (see @blurwind/cli):
blurwind generate2. Bind the manifest once and export a component:
// components/Image.ts
import { createBlurImage } from '@blurwind/next'
import { BLUR_DATA_URLS } from '@/app/constants/blur-data'
export default createBlurImage({
manifest: BLUR_DATA_URLS,
baseURL: process.env.ASSET_CDN_PATH // prepended to every src
})3. Use it exactly like next/image:
import Image from '@/components/Image'
<Image src="/hero.webp" alt="Hero" width={1200} height={600} />By default the manifest placeholder is rendered through next/image's native
placeholder="blur", so the component is a true drop-in: every prop
(className, style, fill, width, height, sizes, …) is forwarded to the
image untouched. Sources without a generated placeholder fall back to a plain
next/image (honouring any placeholder / blurDataURL you pass).
Prefer an animated cross-fade over next/image's built-in blur-up? Opt in per binding:
export default createBlurImage({
manifest: BLUR_DATA_URLS,
animation: { type: 'fade' } // motion cross-fade instead of native blur
})Do this once, use it everywhere. Bind the component in a single file and replace
import Image from 'next/image'withimport Image from '@/components/Image'across your app. Every teammate then gets automatic placeholders for free — no per-call setup, noblurDataURLwiring.
Gotchas
These are the things that surprise people. Read them before filing a bug.
baseURL must be readable in the browser
The component renders on the client, so a baseURL that comes from
process.env must be inlined into the browser bundle — a bare server-only
env var will be undefined at render and your images will 404. Use one of:
// next.config.js — expose it to the client
const nextConfig = { env: { ASSET_CDN_PATH: process.env.ASSET_CDN_PATH } }or a NEXT_PUBLIC_-prefixed variable (process.env.NEXT_PUBLIC_ASSET_CDN_PATH).
Use the same value in blurwind.config.ts so the CLI fetches from where the
component loads from.
fill and className (the default is a true drop-in)
Out of the box the component forwards every prop straight to next/image —
fill, className, style, width, height, sizes. So a fill-heavy app
just works:
<Image fill className="object-cover" sizes="100vw" src="/hero.webp" alt="Hero" />object-cover lands on the <img>, and blurwind never injects a height, so you
won't hit next/image's "has both height and fill" error. No layout changes,
no wrapper.
The animation: { type: 'fade' } opt-in
The motion cross-fade renders the image inside a positioned <div> so the blurred
overlay can fade out. className / style still reach the <img> (not the
wrapper), but the wrapper does introduce a layout box. For fill images it fills
the positioned parent (same contract as bare next/image fill); for intrinsic
images it sizes to the image. If you want a placeholder with zero layout
involvement, prefer the default (native) mode — it's the genuine drop-in.
Static imports
src also accepts a static import (StaticImageData) — the same value you'd pass
to next/image. These are handed straight to next/image (no baseURL prefix, no
manifest lookup), and their build-time blurDataURL is reused as the placeholder:
import Image from '@/components/Image'
import hero from '@/assets/hero.png' // StaticImageData
<Image src={hero} alt="Hero" />So one component covers both remote/CDN strings and local static assets — no
need to keep next/image around for imported images.
API
createBlurImage(options)
| Option | Type | Description |
| --- | --- | --- |
| manifest | AnyManifest | The generated manifest (flat map or versioned). |
| baseURL | string? | Prefix prepended to each src to build the final URL. |
| animation | Partial<AnimationConfig>? | Placeholder transition. Default: native next/image blur (true passthrough). Set { type: 'fade' } for the motion cross-fade (400ms anticipate). |
Returns a component whose props are next/image's props plus:
src— astringmatched against the manifest keys, or a static import (StaticImageData) passed straight tonext/image.srcDark— optional dark-theme source (string or static import), rendered viadark:visibility classes so both variants are present for instant theme switching.
License
MIT © Benyamin Khodadadi
