Primedocs
Components

Image

Reusable image component with LQIP support, fallback states, and skeleton loading

Hey there

A powerful image component that extends Next.js Image with built-in loading states, error fallbacks, and Low Quality Image Placeholder (LQIP) support.

Features

  • LQIP Support — Built-in blur-up loading effect with blurDataURL
  • Fallback States — Automatic error handling with ImageOff icon
  • Skeleton Loading — Smooth loading animation while image fetches
  • Type Safe — Full TypeScript support with Next.js Image props

Installation

npx shadcn@latest add https://ui.quickprimetech.com/r/image.json
Open in

"use client";
import NextImage from "next/image";
import { ComponentProps, useState } from "react";
import { ImageOff, LucideProps } from "lucide-react";
import { cn } from "@/lib/utils";
import { Skeleton } from "./skeleton";

type ImageWithFallbackProps = Omit<
React.ComponentProps<typeof NextImage>,
"src"

> & {
> src: string | null;
> iconProps?: Partial<LucideProps>;
> textProps?: ComponentProps<"span">;
> };

export function Image({
src,
alt,
iconProps,
textProps,
...props
}: ImageWithFallbackProps) {
const [imageError, setImageError] = useState(false);
const [isLoading, setIsLoading] = useState(true);

// Show fallback if src is null or empty, or if image fails to load
const showFallback = !src || imageError;

if (showFallback) {
return (

<div className="w-full h-full bg-muted flex flex-col items-center justify-center text-muted-foreground">
<ImageOff
className={cn("size-8 mb-2 opacity-50", iconProps?.className)}
{...iconProps}
/>
<span
className={cn("text-sm font-medium", textProps?.className)}
{...textProps} >
{!src ? "No Image" : "Failed to load"}
</span>
</div>
);
}

return (

<div className="relative w-full h-full">
{/* Show skeleton while loading if no LQIP */}
{isLoading && !props.blurDataURL && (
<Skeleton className="absolute inset-0 size-full" />
)}

  <NextImage
    src={src as string}
    alt={alt || "Image"}
    {...props}
    onLoad={() => setIsLoading(false)}
    onError={() => setImageError(true)}
    className={cn(
      "transition-opacity duration-300",
      isLoading ? "opacity-0" : "opacity-100",
      props.className,
    )}
  />
</div>

);
}

Examples

Image with LQIP

A base64 blur placeholder is shown instantly while the full image loads, then fades out.

Food dish
<div className="relative w-48 aspect-video">
  <Image
    src="https://res.cloudinary.com/quick-prime-tech/image/upload/v1765984117/ziwa-restaurant/eldoret/menu-items/hmpmvym4weszxx0ohh4e.jpg"
    alt="Food dish"
    blurDataURL="data:image/jpeg;base64,..."
    placeholder="blur"
    fill
    className="object-cover rounded-md"
  />
</div>

Skeleton loading

When no blurDataURL is provided, an animated skeleton shimmer is shown while the image fetches.

Food dish
<div className="relative w-48 aspect-video">
  <Image
    src="https://res.cloudinary.com/quick-prime-tech/image/upload/v1765984117/ziwa-restaurant/eldoret/menu-items/hmpmvym4weszxx0ohh4e.jpg"
    alt="Food dish"
    fill
    className="object-cover rounded-md"
  />
</div>

Null source

Passing null as src immediately renders the "No Image" fallback — no network request is made.

No Image
<div className="relative w-48 aspect-video">
  <Image src={null} alt="placeholder" fill />
</div>

Broken source

If the image URL fails to load, the component catches the error and renders the "Failed to load" fallback.

broken image
<div className="relative w-48 aspect-video">
  <Image src="https://broken.invalid/image.jpg" alt="broken image" fill />
</div>

Custom fallback

Use iconProps and textProps to restyle or replace the fallback content.

Unavailable
<div className="relative w-48 aspect-video">
  <Image
    src={null}
    alt="custom fallback"
    fill
    iconProps={{ className: "size-6 text-rose-400" }}
    textProps={{
      className: "text-lg text-rose-400",
      children: "Unavailable",
    }}
  />
</div>

API Reference

PropTypeDefaultDescription
srcstring | nullRequired. Image URL. Pass null to show the "No Image" fallback immediately.
altstring"Image"Alt text forwarded to the underlying next/image element.
blurDataURLstringBase64-encoded LQIP string. When provided, activates blur-up — the skeleton is not rendered.
placeholder"blur" | "empty""empty"Pass "blur" alongside blurDataURL to enable the native Next.js blur transition.
iconPropsPartial<LucideProps>Forwarded to the ImageOff icon in fallback states. Override size, color, strokeWidth, etc.
textPropsComponentProps<"span">Forwarded to the fallback label. Override the message via children or restyle with className.
...propsNextImagePropsAll other next/image props — fill, width, height, sizes, priority, className, etc.

On this page