apps/docs/content/docs/react/migration/(components)/image.mdx
v2: <Image> component from @heroui/react
v3: Native HTML `` element or Next.js Image component
The v2 Image component had several features that need to be replaced:
| v2 Feature | v3 Equivalent | Notes |
|---|---|---|
radius prop | rounded-* Tailwind classes | Use rounded-sm, rounded-md, rounded-lg, rounded-full |
shadow prop | shadow-* Tailwind classes | Use shadow-sm, shadow-md, shadow-lg |
isBlurred prop | Manual blur implementation | Use CSS filter: blur() or Tailwind blur-* utilities |
isZoomed prop | Manual hover zoom | Use hover:scale-* Tailwind classes |
fallbackSrc prop | Manual error handling | Use onError handler with state |
disableSkeleton / Loading skeleton | Manual loading state | Use React state + conditional rendering |
removeWrapper prop | Direct rendering | No wrapper needed, render `` directly |
In v2, Image was a component wrapper around the native `` element:
import { Image } from "@heroui/react";
export default function App() {
return (
<Image
src="https://example.com/image.jpg"
alt="Example image"
width={300}
height={200}
/>
);
}
In v3, use the native `` element directly with Tailwind CSS classes:
export default function App() {
return (
);
}
If you're using Next.js, use the optimized Image component:
<Tabs items={["v2", "v3"]}> <Tab value="v2"> ```tsx import { Image } from "@heroui/react";
<Image
src="/image.jpg"
alt="Example"
width={300}
height={200}
/>
```
<Image
src="/image.jpg"
alt="Example"
width={300}
height={200}
className="rounded-lg"
/>
```
<Tabs items={["v2", "v3"]}>
<Tab value="v2">
tsx <Image isZoomed src="..." alt="..." />
</Tab>
<Tab value="v3">
```tsx
<div className="relative overflow-hidden rounded-lg">
</div>
```
<Tabs items={["v2", "v3"]}>
<Tab value="v2">
tsx <Image isBlurred src="..." alt="..." />
</Tab>
<Tab value="v3">
```tsx
<div className="relative">
</div>
```
<Tabs items={["v2", "v3"]}>
<Tab value="v2">
tsx <Image src="https://example.com/image.jpg" fallbackSrc="/fallback.jpg" alt="Example" />
</Tab>
<Tab value="v3">
```tsx
import { useState } from "react";
function ImageWithFallback({ src, fallbackSrc, alt, ...props }) {
const [imgSrc, setImgSrc] = useState(src);
return (
setImgSrc(fallbackSrc)}
{...props}
/>
);
}
<ImageWithFallback
src="https://example.com/image.jpg"
fallbackSrc="/fallback.jpg"
alt="Example"
/>
```
<Tabs items={["v2", "v3"]}>
<Tab value="v2">
tsx <Image src="https://example.com/image.jpg" alt="Example" disableSkeleton={false} />
</Tab>
<Tab value="v3">
```tsx
import { useState } from "react";
function ImageWithSkeleton({ src, alt, ...props }) {
const [isLoading, setIsLoading] = useState(true);
const [hasError, setHasError] = useState(false);
return (
<div className="relative overflow-hidden rounded-lg bg-default-200">
{isLoading && (
<div className="absolute inset-0 animate-pulse bg-gradient-to-r from-transparent via-default-300 to-transparent" />
)}
setIsLoading(false)}
onError={() => {
setIsLoading(false);
setHasError(true);
}}
{...props}
/>
</div>
);
}
<ImageWithSkeleton src="https://example.com/image.jpg" alt="Example" />
```
<Tabs items={["v2", "v3"]}>
<Tab value="v2">
tsx <Image src="https://example.com/image.jpg" alt="Example" radius="lg" shadow="md" isZoomed isBlurred width={400} height={300} />
</Tab>
<Tab value="v3">
```tsx
<div className="relative overflow-hidden rounded-lg shadow-md">
</div>
```
If you frequently use images with similar features, you can create a reusable component:
<Tabs items={["v2", "v3"]}> <Tab value="v2"> ```tsx import { Image } from "@heroui/react";
<Image
src="..."
radius="lg"
shadow="md"
isZoomed
/>
```
interface CustomImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {
radius?: "none" | "sm" | "md" | "lg" | "full";
shadow?: "none" | "sm" | "md" | "lg";
isZoomed?: boolean;
isBlurred?: boolean;
fallbackSrc?: string;
}
const radiusClasses = {
none: "rounded-none",
sm: "rounded-sm",
md: "rounded-md",
lg: "rounded-lg",
full: "rounded-full",
};
const shadowClasses = {
none: "shadow-none",
sm: "shadow-sm",
md: "shadow-md",
lg: "shadow-lg",
};
export function CustomImage({
src,
alt,
className,
radius = "lg",
shadow = "none",
isZoomed = false,
isBlurred = false,
fallbackSrc,
onError,
...props
}: CustomImageProps) {
const [imgSrc, setImgSrc] = useState(src);
const [isLoading, setIsLoading] = useState(true);
const handleError = (e: React.SyntheticEvent<HTMLImageElement, Event>) => {
if (fallbackSrc && imgSrc !== fallbackSrc) {
setImgSrc(fallbackSrc);
}
onError?.(e);
};
const imageElement = (
setIsLoading(false)}
onError={handleError}
{...props}
/>
);
if (isBlurred) {
return (
<div className={cn("relative", radiusClasses[radius], shadowClasses[shadow])}>
{imageElement}
</div>
);
}
if (isZoomed || isLoading) {
return (
<div className={cn("relative overflow-hidden", radiusClasses[radius], shadowClasses[shadow])}>
{isLoading && (
<div className="absolute inset-0 animate-pulse bg-gradient-to-r from-transparent via-default-300 to-transparent" />
)}
{imageElement}
</div>
);
}
return imageElement;
}
// Usage
<CustomImage
src="..."
alt="Example"
radius="lg"
shadow="md"
isZoomed
isBlurred
fallbackSrc="/fallback.jpg"
/>
```
<Tabs items={["v2", "v3"]}> <Tab value="v2"> ```tsx import { Image } from "@heroui/react";
export default function App() {
return (
<div className="space-y-4">
<Image
src="https://example.com/image1.jpg"
alt="Image 1"
width={300}
height={200}
radius="lg"
shadow="md"
/>
<Image
src="https://example.com/image2.jpg"
alt="Image 2"
width={300}
height={200}
isZoomed
radius="lg"
/>
<Image
src="https://example.com/image3.jpg"
alt="Image 3"
width={300}
height={200}
isBlurred
radius="full"
/>
</div>
);
}
```
<div className="relative overflow-hidden rounded-lg">
</div>
<div className="relative rounded-full">
</div>
</div>
);
}
```
Image component no longer exists in v3import { Image } from "@heroui/react"ImageImage from @heroui/react imports<Image> instances with `` elementsnext/image for optimizationIf you're using Next.js, the Image component from next/image provides:
srcSetimport Image from "next/image";
<Image
src="/image.jpg"
alt="Example"
width={300}
height={200}
className="rounded-lg shadow-md"
placeholder="blur" // Optional: blur placeholder
blurDataURL="data:image/..." // Optional: base64 blur data
/>