Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How should I use next/image? #368

Closed
angelhodar opened this issue May 17, 2023 · 8 comments
Closed

How should I use next/image? #368

angelhodar opened this issue May 17, 2023 · 8 comments

Comments

@angelhodar
Copy link

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!

@angelhodar
Copy link
Author

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

@raffizulvian
Copy link

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:
visual comparison

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

@angelhodar
Copy link
Author

@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?

@raffizulvian
Copy link

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.

@wellsm
Copy link

wellsm commented Apr 20, 2024

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>

@avi-mukesh
Copy link

What folder should the image be placed? Inside public?

@totalolage
Copy link

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>
  );
}

@marjorg
Copy link

marjorg commented Sep 4, 2024

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>

Just a heads up, this will load the image twice. Once by Radix and once by Next

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants