Skip to content

Components

A drop-in image with a BlurHash placeholder and a drag-and-drop uploader, installed with the shadcn CLI and owned as source in your repo.

Rendering images is <AuraImage />. Accepting them is <AuraUploader />. Neither depends on the other, and both read your CDN base URL from NEXT_PUBLIC_AURA_CDN_URL.

Installing either one copies a single .tsx file into your repo. There is no @auraimage/react package to pin, and no component API you have to wait on us to extend — edit the file, delete half of it, restyle it; it's yours.

AuraImage

<AuraImage />
Overlapping grey and crimson polygons on a dark textured ground, served from the AuraImage edge.

https://cdn.auraimage.ai/my-project/w=1536,h=1024,q=80/demo/0049

The default. A 28-character hash decodes to a canvas on the client and crossfades into the full image — the smallest placeholder of the three. Falls back to LQIP if the hash is missing or malformed.

A drop-in replacement for <img> that paints a placeholder on mount and crossfades into the full image once it decodes — the box is reserved from the start, so nothing reflows and the LCP element never flashes empty. The three placeholder modes — blurhash, lqip, and empty — trade payload against fidelity; the demo above switches between them live, and the URL under the frame is the exact request the component makes.

Transform options travel in the URL path, so a resize is a different URL rather than a different build. With the default format="auto" the extension is omitted entirely and the edge negotiates AVIF, WebP, or JPEG per request.

The w=1536,h=1024 above is not arbitrary: the edge rounds both dimensions up to a fixed dimension ladder, and those two values sit on it exactly. Ask for w=800,h=533 instead and you'd be served a 1024×768 box — larger than requested, and cropped to a different aspect ratio. Open the network tab and check this one; the URL under the frame is what arrives.

npx shadcn@latest add https://auraimage.ai/registry/image.json

Writes components/aura/image.tsx, installs blurhash from npm, and pulls in the config module. No Next.js import and no Tailwind requirement.

Every <AuraImage /> prop →

AuraUploader

<AuraUploader />
Drop a real image. It uploads to production through the shared demo project, and the exact UploadResult your onUpload handler would receive is printed here.

Drag-and-drop, click-to-browse, and paste-from-clipboard, backed by a multi-file queue: per-row thumbnail and progress bar, cancel and retry, drag-reorder of files still waiting, and a copy-URL pill on each finished row. Uploads run in parallel up to concurrency (default 8).

Bytes go straight from the browser to cdn.auraimage.ai. Your backend only mints a short-lived signature — it never proxies the file. The demo above signs through the shared demo project; in your app you'd pass project="my-app", a pre-computed signature, or your own getSignature function.

npx shadcn@latest add https://auraimage.ai/registry/uploader.json

Writes components/aura/uploader.tsx and installs lucide-react, @auraimage/sdk, the shadcn cn() helper, and the config module. One command, nothing left to wire.

To see an uploaded image go straight into a transform pipeline, the home page pairs the same uploader with a live playground for width, quality, format, and fit.

Full props reference →

Installing

The config module

Both components resolve the CDN origin through @/lib/config.public, which the registry installs alongside them as a dependency. You don't need to create it — but you should know what it is, because it's the one file you'll edit if your setup differs:

src/lib/config.public.ts
export const publicConfig = {
  NEXT_PUBLIC_AURA_CDN_URL: (process.env.NEXT_PUBLIC_AURA_CDN_URL ?? '').replace(/\/+$/, '')
};

That single field is all either component reads. On a bundler that doesn't expose process.env — Vite, where it's import.meta.env — change this line and both components follow.

From a URL

Every registry item has a stable JSON endpoint. This is the copy-paste form used above and it needs no configuration:

npx shadcn@latest add https://auraimage.ai/registry/image.json
npx shadcn@latest add https://auraimage.ai/registry/uploader.json

As a named registry

If you install from AuraImage more than once, register the namespace in your components.json and refer to items by short name:

components.json
{
  "registries": {
    "@aura": "https://auraimage.ai/registry/{name}.json"
  }
}
npx shadcn@latest add @aura/image @aura/uploader

What lands in your repo

ItemFiles writtennpm packages installed
imagecomponents/aura/image.tsx, lib/config.public.tsblurhash
uploadercomponents/aura/uploader.tsx, lib/config.public.ts, lib/utils.tslucide-react, @auraimage/sdk, clsx, tailwind-merge

Paths follow your components.json aliases, so the files land wherever the rest of your components live. Installing both items writes lib/config.public.ts once, not twice.

Requirements

  • React 18 or newer. Both are client components ('use client') built on hooks and plain DOM APIs. Neither imports from next/*, so they run under Vite or Remix as readily as Next.js — the NEXT_PUBLIC_ prefix is a naming convention here, not a framework dependency.
  • NEXT_PUBLIC_AURA_CDN_URL in your environment, read through the config module. aura init writes it to .env.local alongside your project name and public key; see Getting Started. This is the only thing the installer can't do for you.
  • Tailwind CSS<AuraUploader /> only, for its styling. <AuraImage /> uses inline styles and passes className straight through, so it drops into any styling approach.

Why source instead of a package

A CDN component is exactly the kind of code you end up wanting to change: a different placeholder ramp, your own loading skeleton, an extra sizes attribute, a wrapper your design system already has. Shipping it as a dependency means every one of those is a feature request. Shipping it as source means it's a diff.

The tradeoff is upgrades: re-running the add command overwrites the file, so your edits are yours to reconcile. Both components are single files under 500 lines, which is the point — you can read the whole thing before you accept it.