ui
ui copied to clipboard
How should I use next/image?
Hey! First of all thanks for this amazing library. I was looking for them to use in my project and I am just wondering how the AvatarImage component integrates with the next/image component. I am using it in my project so I am worried about poor performance when using this component on anyone that accepts an image src as prop.
Thanks in advance!
I think next/image can be used as src prp, for the AvatarImage component. And this will help us utilize some next/image perks like lazy loading, resizing of image
Mmm i dont understand what do you mean by using as src prop. Src prop has to be a string i think, not a component
You could provide an asChild
prop to the AvatarImage
component. Since shadcn-ui is built on top of Radix UI, all valid Radix UI props are also valid shadcn-ui props.
Here's how you can use it:
<Avatar>
<AvatarImage asChild src='/next.svg'>
<Image src='/next.svg' alt='logo' width={40} height={40} />
</AvatarImage>
<AvatarFallback>N</AvatarFallback>
</Avatar>
Notice that I provide src
prop twice in both AvatarImage
and Image
components. If AvatarImage
does not receive a src
prop, it will automatically use AvatarFallback
. Unfortunately, it seems the only way to make the intended results.
After I checked the rendered HTML, the result is the same as the Image component. For more information check Radix UI Avatar API Reference and documentation on how to use asChild
prop.
Comparison
Here are comparisons between different configurations to help see the difference:
The code:
<div className='border rounded w-56 border-stone-800 p-4'>
<p>Next Image</p>
<Image src='/next.svg' alt='logo' width={40} height={40} className='my-4'><Image />
</div>
<div className='border rounded w-56 border-stone-800 p-4'>
<p>shadcn-ui Avatar</p>
<Avatar>
<AvatarImage src='/next.svg' alt='logo' />
<AvatarFallback>N</AvatarFallback>
</Avatar>
</div>
<div className='border rounded w-56 border-stone-800 p-4'>
<p>shadcn-ui Avatar + img tag</p>
<Avatar>
<AvatarImage asChild src='/next.svg'>
<img src='/next.svg' alt='logo' />
</AvatarImage>
<AvatarFallback>N</AvatarFallback>
</Avatar>
</div>
<div className='border rounded w-56 border-stone-800 p-4'>
<p>shadcn-ui Avatar + Next Image (asChild, without src)</p>
<Avatar>
<AvatarImage asChild>
<Image src='/next.svg' alt='logo' width={40} height={40} />
</AvatarImage>
<AvatarFallback>N</AvatarFallback>
</Avatar>
</div>
<div className='border rounded w-56 border-stone-800 p-4'>
<p>shadcn-ui Avatar + Next Image (asChild, with src)</p>
<Avatar>
<AvatarImage asChild src='/next.svg'>
<Image src='/next.svg' alt='logo' width={40} height={40} />
</AvatarImage>
<AvatarFallback>N</AvatarFallback>
</Avatar>
</div>
Rendered HTML:
<div class="border rounded w-56 border-stone-800 p-4">
<p>Next Image</p>
<img
alt="logo"
loading="lazy"
width="40"
height="40"
decoding="async"
data-nimg="1"
class="my-4"
style="color: transparent"
src="/next.svg" />
</div>
<div class="border rounded w-56 border-stone-800 p-4">
<p>shadcn-ui Avatar</p>
<span class="relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full">
<img class="aspect-square h-full w-full" alt="logo" src="/next.svg"/>
</span>
</div>
<div class="border rounded w-56 border-stone-800 p-4">
<p>shadcn-ui Avatar + img tag</p>
<span class="relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full">
<img src="/next.svg" alt="logo" class="aspect-square h-full w-full"/>
</span>
</div>
<div class="border rounded w-56 border-stone-800 p-4">
<p>shadcn-ui Avatar + Next Image (asChild, without src)</p>
<span class="relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full">
<span class="flex h-full w-full items-center justify-center rounded-full bg-muted">
N
</span>
</span>
</div>
<div class="border rounded w-56 border-stone-800 p-4">
<p>shadcn-ui Avatar + Next Image (asChild, with src)</p>
<span class="relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full">
<img
alt="logo"
loading="lazy"
width="40"
height="40"
decoding="async"
data-nimg="1"
class="aspect-square h-full w-full"
src="/next.svg"
style="color: transparent"/>
</span>
</div>
Hope this helps @angelhodar
@raffizulvian Thank you so much for the detailed explanation!!! I think I just have to try to understand the asChild
prop more in depth. Maybe @shadcn wants to add this to the Avatar
documentation?
You're welcome! To add a little bit, apparently, the reason why we need to provide src
twice is the default behavior of Radix UI's Avatar.
There is already someone who opens a discussion here.
You could use Suspense
for this
Code:
<Avatar>
<Suspense fallback={<AvatarFallback>LG</AvatarFallback>}>
<Image src="/next.svg" alt="logo" width={50} height={50} />
</Suspense>
</Avatar>
What folder should the image be placed? Inside public?
What folder should the image be placed? Inside public?
Best to import it statically and let next sort it
import image from "./image.jpeg";
....
<Image src={image} />
I wrote a little component that lets you use Avatar
like a next Image
:
// components/ui/nextAvatar.tsx
import { ImageProps, getImageProps } from "next/image";
import { omit, pick } from "lodash/fp";
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar";
type Props = Omit<ImageProps, "fill">;
export default function NextAvatar(props: Props) {
const imageProps = getImageProps({ width: 40, height: 40, ...props }).props;
return (
<Avatar
className={props.className}
style={pick(["width", "height"], imageProps)}
>
<AvatarImage
{...omit(["blurWidth", "blurHeight", "style"], imageProps)}
style={pick(["objectFit", "objectPosition"], imageProps.style)}
/>
{imageProps.placeholder === "blur" && (
<AvatarFallback style={imageProps.style} />
)}
</Avatar>
);
}