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

[Avatar] Allow Avatar.Image to be supplied a component to render the image #2230

Open
mririgoyen opened this issue Jun 26, 2023 · 14 comments
Open
Labels
Package: react/avatar Type: Enhancement Small enhancement to existing primitive/feature

Comments

@mririgoyen
Copy link

Feature request

Overview

The Avatar.Image primitive assumes I want to use an img without the ability to override that. I'd like to use the Next.js Image component instead for its optimizations. If I try to use the Next Image component instead of the primitive, the Fallback fails to work properly.

Examples in other libraries

Other libraries, such as Material UI, allow you to pass an optional component as a prop.

<Image
  alt={altText}
  className="aspect-square h-full w-full"
  component={NextImage} // Provide a React component or a string value of an HTML element, default would be "img"
  src={src}
/>

Who does this impact? Who is this for?

This would affect anyone utilizing the Next.js framework who wants to use it's image optimizations.

Other Notes

If there are any other primitives that make use of images, allowing overrides on them would be greatly appreciated.

@joaom00
Copy link
Contributor

joaom00 commented Jun 26, 2023

Did you try this?

<Avatar.Image asChild>
  <NextImage />
</Avatar.Image>

@mririgoyen
Copy link
Author

Did you try this?

<Avatar.Image asChild>
  <NextImage />
</Avatar.Image>

This will throw type errors because none of the required props are being supplied to NextImage.

@zaunermax
Copy link

Did you try this?

<Avatar.Image asChild>
  <NextImage />
</Avatar.Image>

For me this does not throw type errors, it just outright does not work. Instead it renders the AvatarFallback instead.

@raffizulvian
Copy link

I faced a similar issue. My workaround was to also supply src to Avatar.Image and NextImage.

<Avatar.Image asChild src='image.svg'>
  <NextImage src='image.svg' alt='Some alt text' {...otherProps} />
</Avatar.Image>

It's a little bit redundant, but it could be wrapped inside a wrapper component.

I think it would be nice to make src not required if asChild is present or use a solution like other libraries as stated above by the author.

@trm217
Copy link

trm217 commented Aug 24, 2023

Another issue with having to supply the src on Avatar.Image, is that asset imports, which enables further build-time optimisation, is not possible as Avatar.Image would not accept it for the src value.

@akhan619
Copy link

I faced a similar issue. My workaround was to also supply src to Avatar.Image and NextImage.

<Avatar.Image asChild src='image.svg'>
  <NextImage src='image.svg' alt='Some alt text' {...otherProps} />
</Avatar.Image>

It's a little bit redundant, but it could be wrapped inside a wrapper component.

I think it would be nice to make src not required if asChild is present or use a solution like other libraries as stated above by the author.

This is not only redundant but highly inefficient. Just checked devtools and both the avatar and next-image components load the asset. So we end up loading the optimized image by next-image and then the avatar-image also loads the image. So, in the end there are 2 requests for the same image.

@akhan619
Copy link

The solution i am using currently is to not use the Avatar.Image at all and just use the NextImage and Avatar.Fallback . Just to give a barebones example:

<Avatar.Root>
    {imageSrc && <NextImage src={imageSrc} alt='Some alt text' {...otherProps} />}
    {!imageSrc && <Avatar.Fallback>John Doe</Avatar.Fallback>}
</Avatar.Root>

If imageSrc is not valid or there are errors loading the image then NextImage error handling and some state variables can be used to handle the case.

@DasOhmoff
Copy link

DasOhmoff commented Jan 11, 2024

I am facing the same issue. Fixing this would make radix way more viable for the people

@jonathanlal
Copy link

agreed, would be a really nice feature

@benoitgrelard benoitgrelard added Type: Enhancement Small enhancement to existing primitive/feature Package: react/avatar labels Mar 5, 2024
@mehrabmp
Copy link

mehrabmp commented Mar 9, 2024

Any update on this?

@valenciaHQ
Copy link

Any update?

@jynxio
Copy link

jynxio commented May 26, 2024

How to use custom components?

import NextImage from 'next/image';
import * as Avatar from '@radix-ui/react-avatar';

const App = () => (
    <Avatar.Root>
        <Avatar.Image src="./img/url" alt="" width={20} height={20} asChild>
            <Image />
        </Avatar.Image>
        <Avatar.Fallback delayMs={600}>Fallback</Avatar.Fallback>
    </Avatar.Root>
);

If you are using TypeScript, you can refer to shadcn/ui Avatar to resolve type issues.


Why does the image load twice?

I couldn't resolve this bug, but it seems to be related to Next.js rather than Radix Primitive.

I ran the following demo in both pure React and Next.js environments, and found that the pure React application loaded the image only once, while the Next.js one loaded it twice.

"use client"; // just for next.js

const CustomImg = React.forwardRef((props, forwardRef) => <img {...props} ref={forwardedRef} />);
const Page = () => (
    <Avatar.Root>
        <Avatar.Image src="./img/url" alt="" asChild>
            <CustomImg />
        </Avatar.Image>
        <Avatar.Fallback delayMs={600}>Fallback</Avatar.Fallback>
    </Avatar.Root>
);

Additionally, from the source code, it appears that the Avatar component is not the issue, because the part that handles asChild is written like this in the Primitive.tsx file:

 const Primitive = NODES.reduce((primitive, node) => {
     const Node = React.forwardRef(
         (props: PrimitivePropsWithRef<typeof node>, forwardedRef: any) => {
             const { asChild, ...primitiveProps } = props;
+            const Comp: any = asChild ? Slot : node;

             React.useEffect(() => {
                 (window as any)[Symbol.for('radix-ui')] = true;
             }, []);

             return <Comp {...primitiveProps} ref={forwardedRef} />;
         },
     );

     Node.displayName = `Primitive.${node}`;

     return { ...primitive, [node]: Node };
 }, {} as Primitives);

@jynxio
Copy link

jynxio commented May 26, 2024

The solution i am using currently is to not use the Avatar.Image at all and just use the NextImage and Avatar.Fallback . Just to give a barebones example:

<Avatar.Root>
    {imageSrc && <NextImage src={imageSrc} alt='Some alt text' {...otherProps} />}
    {!imageSrc && <Avatar.Fallback>John Doe</Avatar.Fallback>}
</Avatar.Root>

If imageSrc is not valid or there are errors loading the image then NextImage error handling and some state variables can be used to handle the case.

Unfortunately, this approach will cause the Fallback feature to fail 😢.

@Udhayarajan
Copy link

Additionally, from the source code, it appears that the Avatar component is not the issue, because the part that handles asChild is written like this in the Primitive.tsx file:

 const Primitive = NODES.reduce((primitive, node) => {
     const Node = React.forwardRef(
         (props: PrimitivePropsWithRef<typeof node>, forwardedRef: any) => {
             const { asChild, ...primitiveProps } = props;
+            const Comp: any = asChild ? Slot : node;

             React.useEffect(() => {
                 (window as any)[Symbol.for('radix-ui')] = true;
             }, []);

             return <Comp {...primitiveProps} ref={forwardedRef} />;
         },
     );

     Node.displayName = `Primitive.${node}`;

     return { ...primitive, [node]: Node };
 }, {} as Primitives);

According to changes made in #2999, this code is no longer executed via Avatar.Image.

What I mean is, Avatar.Image
creates the clone of the child node and returns & renders, unlike the current approach where the child is rendered from
Primitive.tsx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Package: react/avatar Type: Enhancement Small enhancement to existing primitive/feature
Projects
None yet
Development

No branches or pull requests