Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .changeset/dry-cups-reply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
"@radix-ui/react-avatar": minor
"radix-ui": minor
---

Added a new `mode` prop to `Avatar.Image` to allow for more control over the image rendering.

- `mode="default"` (default): Matches the existing behavior by rendering an `img` element conditionally based on the loading state of an `Image` object constructed after hydration.
- `mode="native"`: Renders an `img` element unconditionally using its event handlers to update loading state. In `native` mode, you can use CSS to target the image element and style it based on its loading state.
```tsx
<Avatar.Root className="image-root">
<Avatar.Image
mode="native"
className="image"
alt="John Smith"
src="./avatar.png"
/>
<Avatar.Fallback className="fallback">
<AvatarIcon />
</Avatar.Fallback>
</Avatar.Root>
```
```css
.image-root {
position: relative;
}
.image[data-radix-avatar-loading-status]:not(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data-radix-avatar-loading-status diverges from the data-state convention for consumer-facing state elsewhere (Progress exposes its loading lifecycle that way too) — I'm guessing this is an intentional direction change?

Also these are necessary to avoid duplicate UI, so it should maybe be called out more loudly.

[data-radix-avatar-loading-status="loaded"]
) {
/* hide the element visually until it's loaded to reveal the fallback */
position: absolute;
inset: 0;
opacity: 0;
}
```
- `mode="custom"`: Allows for more control over the image rendering. The `render` prop must be provided to render the image. This mode is useful when you want to use a custom image component, such as framework-specific image components or a design-system implementation that uses non-standard props.

```tsx
<Avatar.Image
mode="custom"
className="image"
alt="John Smith"
src="./avatar.png"
render={({ props, ref, context }) => (
<CustomImage
{...props}
ref={ref}
data-loading-status={context.loadingStatus}
onLoadingComplete={({ naturalWidth, naturalHeight }) => {
context.onLoadingStatusChange("loaded");
}}
onError={() => {
context.onLoadingStatusChange("error");
}}
/>
)}
/>
```
8 changes: 8 additions & 0 deletions apps/storybook/stories/avatar.stories.module.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.root {
position: relative;
/* ensures image/fallback is centered */
display: inline-flex;
align-items: center;
Expand All @@ -21,6 +22,13 @@
object-fit: cover;
}

.image[data-radix-avatar-loading-status]:not([data-radix-avatar-loading-status='loaded']),
.image[data-loading-status]:not([data-loading-status='loaded']) {
opacity: 0;
position: absolute;
inset: 0;
}

.fallback {
/* ensures content inside the fallback is centered */
width: 100%;
Expand Down
Loading
Loading