primitives
primitives copied to clipboard
[Avatar] Allow `Avatar.Image` to be supplied a `component` to render the image
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.
Did you try this?
<Avatar.Image asChild>
<NextImage />
</Avatar.Image>
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.
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.
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.
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.
I faced a similar issue. My workaround was to also supply
srctoAvatar.ImageandNextImage.<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
srcnot required ifasChildis 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.
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.
I am facing the same issue. Fixing this would make radix way more viable for the people
agreed, would be a really nice feature
Any update on this?
Any update?
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);
The solution i am using currently is to not use the
Avatar.Imageat all and just use theNextImageandAvatar.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
imageSrcis not valid or there are errors loading the image thenNextImageerror handling and some state variables can be used to handle the case.
Unfortunately, this approach will cause the Fallback feature to fail 😢.