Skip to content

Conversation

@Arnab-Mandal1
Copy link
Collaborator

@Arnab-Mandal1 Arnab-Mandal1 commented Jan 15, 2026

Description

Introduced a reusable Avatar component to display user profile images or fallback initials. This component is designed to be an atom within the design system, supporting multiple predefined sizes and custom styling options. It handles cases where an image source is missing by rendering a styled fallback with initials.

Type of Change

  • New feature
  • Bug fix
  • Refactoring
  • Documentation update
  • Test addition/update

Changes Made

  • Created AvatarProps.ts to define AvatarSize types and component interface.
  • Implemented Avatar.tsx with conditional rendering for images and text fallbacks.
  • Defined a sizeMap to handle standard sizes (xs, sm, md, lg, xl).
  • Added comprehensive JSDoc comments with usage examples.
  • Applied default styling for circular shape and centered content in fallback mode.

Testing Done

  • All tests pass
  • Added new tests
  • Manual testing completed

Screenshots (if applicable)

N/A

Related Issues

N/A

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Props marked as read-only where applicable
  • No console.log or debugging code in production logic
  • Storybook stories updated to reflect component usage
  • All tests passing

Summary by CodeRabbit

  • New Features

    • Added an Avatar component that displays an image or a text fallback (up to two characters or "?" on failure), recovers from broken images, includes accessibility labeling, and supports five sizes (XS, SM, MD, LG, XL).
  • Documentation

    • Added Storybook stories demonstrating default fallback, image and broken-image behavior, and a showcase of all sizes.

✏️ Tip: You can customize this high-level summary in your review settings.

This commit introduces a new atomic `Avatar` component, designed to display user avatars.

Key features include:
- **Image display:** Renders an `<img>` element when a `src` prop is provided.
- **Fallback mechanism:** Displays initials or a single character as a fallback when `src` is not available or fails to load.
- **Multiple sizes:** Supports predefined sizes (`xs`, `sm`, `md`, `lg`, `xl`) for consistent scaling.
- **Accessibility:** Includes `alt` text for images and `aria-label` for fallback elements.
- **Storybook integration:** Provides comprehensive stories demonstrating various use cases, including default, image, broken image, and size variations.

This component enhances the UI library by providing a flexible and robust solution for displaying user profiles.
@Arnab-Mandal1 Arnab-Mandal1 self-assigned this Jan 15, 2026
@Arnab-Mandal1 Arnab-Mandal1 added the enhancement New feature or request label Jan 15, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 15, 2026

📝 Walkthrough

Walkthrough

Adds a new Avatar component with its props/type definitions and Storybook stories. Renders an image when available, falls back to initials (or "?") on missing/broken images, manages load errors, and supports five sizes (xs–xl).

Changes

Cohort / File(s) Summary
Avatar types & component
src/components/atoms/Avatar/AvatarProps.ts, src/components/atoms/Avatar/Avatar.tsx
New AvatarSize type and AvatarProps interface; new Avatar React component that displays an image or an initials/text fallback, handles image load errors (state reset on src change), supports alt, className, and sizes xs/sm/md/lg/xl.
Storybook stories
src/components/atoms/Avatar/Avatar.stories.tsx
New Storybook meta export and Story type alias; four stories added: Default, WithImage, BrokenImage, and Sizes (custom render showing all sizes and args per story).

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • dbc2201
  • rohit3171999
  • KARUNANS2004

Poem

🐰
I nibble code and circle round,
When pictures fail, my letters sound.
From tiny xs to big XL cheer,
A hopping avatar appears — hooray, my dear! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(avatar): Introduce Avatar component with image and fallback support' accurately and specifically describes the main change—adding a new Avatar component with image and fallback rendering capabilities, which aligns perfectly with the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings


📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ac4e60 and 45437d6.

📒 Files selected for processing (1)
  • src/components/atoms/Avatar/Avatar.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/atoms/Avatar/Avatar.tsx

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/components/atoms/Avatar/Avatar.tsx`:
- Around line 49-63: The img rendering branch in the Avatar component doesn't
handle load failures; add a local state (e.g., hasImageError via useState) in
the Avatar component, attach an onError handler to the <img> element (and
optionally onLoad to reset the flag) to set hasImageError = true when loading
fails, and change the render logic to show the existing fallback UI whenever src
is falsy or hasImageError is true (this will make the BrokenImage story actually
display the fallback).
🧹 Nitpick comments (2)
src/components/atoms/Avatar/Avatar.tsx (2)

57-60: Prefer TailwindCSS utility classes over inline styles.

The coding guidelines state: "Use TailwindCSS v4 utility classes exclusively for styling" and "Use DaisyUI component classes." The current implementation uses inline style objects for borderRadius, objectFit, flexbox layout, colors, and typography.

Consider refactoring to use Tailwind classes and building dynamic class names using arrays.

♻️ Suggested refactor using TailwindCSS
 // IMAGE RENDER
 if (src && !imgError) {
+    const imgClasses = [
+        "rounded-full",
+        "object-cover",
+        className,
+    ].filter(Boolean).join(" ");
+
     return (
         <img
             src={src}
             alt={alt}
             width={pixelSize}
             height={pixelSize}
-            className={className}
+            className={imgClasses}
             onError={() => setImgError(true)}
-            style={{
-                borderRadius: "50%",
-                objectFit: "cover",
-            }}
         />
     );
 }

 // FALLBACK RENDER
+const fallbackClasses = [
+    "rounded-full",
+    "flex",
+    "items-center",
+    "justify-content",
+    "bg-gray-200",
+    "text-gray-700",
+    "font-semibold",
+    "select-none",
+    className,
+].filter(Boolean).join(" ");
+
 return (
     <div
         aria-label={alt}
-        className={className}
+        className={fallbackClasses}
         style={{
             width: pixelSize,
             height: pixelSize,
-            borderRadius: "50%",
-            display: "flex",
-            alignItems: "center",
-            justifyContent: "center",
-            backgroundColor: "#e5e7eb",
-            color: "#374151",
-            fontWeight: 600,
             fontSize: Math.floor(pixelSize / 2.5),
-            userSelect: "none",
         }}
     >
         {fallback?.slice(0, 2).toUpperCase() ?? "?"}
     </div>
 );

Note: Dynamic fontSize based on pixelSize may still need an inline style or a size-specific class map.

Also applies to: 70-82


66-86: Consider adding role="img" to the fallback div.

The fallback <div> has aria-label but lacks role="img". Without the role, screen readers may not announce the aria-label correctly since <div> elements don't have an implicit role that uses aria-label.

♻️ Suggested accessibility improvement
 return (
     <div
+        role="img"
         aria-label={alt}
         className={className}
         style={{
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 14b37ae and 9f5f704.

📒 Files selected for processing (3)
  • src/components/atoms/Avatar/Avatar.stories.tsx
  • src/components/atoms/Avatar/Avatar.tsx
  • src/components/atoms/Avatar/AvatarProps.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use TypeScript for all files
Prefer type for unions and primitives, interface for object shapes
Use ?. optional chaining and ?? nullish coalescing

Files:

  • src/components/atoms/Avatar/AvatarProps.ts
  • src/components/atoms/Avatar/Avatar.stories.tsx
  • src/components/atoms/Avatar/Avatar.tsx
src/components/**/*Props.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Props files: PascalCase with Props.ts suffix (e.g., ButtonProps.ts)

Files:

  • src/components/atoms/Avatar/AvatarProps.ts
src/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Other files: camelCase (e.g., setupTests.ts)

Files:

  • src/components/atoms/Avatar/AvatarProps.ts
src/components/**/[!.]*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Define interfaces for all component props in separate [ComponentName]Props.ts files

Files:

  • src/components/atoms/Avatar/Avatar.stories.tsx
  • src/components/atoms/Avatar/Avatar.tsx
src/components/**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/components/**/*.tsx: Use function components (not arrow functions for exported components). Export named components: export function ComponentName() {}
Use destructuring for props, but keep the full props object for clarity in React components
Use React 19 features when appropriate
Use JSDoc comments for component documentation with @example tags
Use TailwindCSS v4 utility classes exclusively for styling
Use DaisyUI component classes (e.g., btn, card, input) and variants (e.g., btn-primary, btn-secondary, btn-sm, btn-md, btn-lg)
Build dynamic class names using arrays and .join(' ')
Component files: PascalCase (e.g., Button.tsx, UserProfile.tsx)
Follow established component directory structure: /src/components/[category]/[ComponentName]/ with ComponentName.tsx, ComponentNameProps.ts, ComponentName.test.tsx, ComponentName.stories.tsx files

Files:

  • src/components/atoms/Avatar/Avatar.stories.tsx
  • src/components/atoms/Avatar/Avatar.tsx
src/**/*.stories.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Story files: PascalCase with .stories.tsx suffix

Files:

  • src/components/atoms/Avatar/Avatar.stories.tsx
🧠 Learnings (6)
📚 Learning: 2025-12-23T08:13:56.218Z
Learnt from: CR
Repo: dbc2201/class-notes PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T08:13:56.218Z
Learning: Applies to src/components/**/[!.]*.tsx : Define interfaces for all component props in separate `[ComponentName]Props.ts` files

Applied to files:

  • src/components/atoms/Avatar/AvatarProps.ts
📚 Learning: 2025-12-23T08:13:56.218Z
Learnt from: CR
Repo: dbc2201/class-notes PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T08:13:56.218Z
Learning: Applies to src/components/**/*Props.ts : Props files: PascalCase with `Props.ts` suffix (e.g., `ButtonProps.ts`)

Applied to files:

  • src/components/atoms/Avatar/AvatarProps.ts
📚 Learning: 2025-12-23T08:13:56.218Z
Learnt from: CR
Repo: dbc2201/class-notes PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T08:13:56.218Z
Learning: Applies to src/components/**/*.tsx : Component files: PascalCase (e.g., `Button.tsx`, `UserProfile.tsx`)

Applied to files:

  • src/components/atoms/Avatar/AvatarProps.ts
  • src/components/atoms/Avatar/Avatar.tsx
📚 Learning: 2025-12-23T08:13:56.218Z
Learnt from: CR
Repo: dbc2201/class-notes PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T08:13:56.218Z
Learning: Applies to **/*.{ts,tsx} : Prefer `type` for unions and primitives, `interface` for object shapes

Applied to files:

  • src/components/atoms/Avatar/AvatarProps.ts
📚 Learning: 2025-12-23T08:13:56.218Z
Learnt from: CR
Repo: dbc2201/class-notes PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T08:13:56.218Z
Learning: Applies to src/**/*.stories.tsx : Story files: PascalCase with `.stories.tsx` suffix

Applied to files:

  • src/components/atoms/Avatar/Avatar.stories.tsx
📚 Learning: 2025-12-23T08:13:56.218Z
Learnt from: CR
Repo: dbc2201/class-notes PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T08:13:56.218Z
Learning: Applies to src/components/**/*.tsx : Use function components (not arrow functions for exported components). Export named components: `export function ComponentName() {}`

Applied to files:

  • src/components/atoms/Avatar/Avatar.tsx
🧬 Code graph analysis (2)
src/components/atoms/Avatar/Avatar.stories.tsx (1)
src/components/atoms/Avatar/Avatar.tsx (1)
  • Avatar (37-87)
src/components/atoms/Avatar/Avatar.tsx (1)
src/components/atoms/Avatar/AvatarProps.ts (2)
  • AvatarSize (1-1)
  • AvatarProps (3-9)
🔇 Additional comments (7)
src/components/atoms/Avatar/AvatarProps.ts (1)

1-9: LGTM!

The type definitions follow the coding guidelines correctly:

  • type for the union (AvatarSize)
  • interface for the object shape (AvatarProps)

Props are appropriately optional with clear documentation for the fallback field.

src/components/atoms/Avatar/Avatar.tsx (3)

1-9: LGTM!

Clean type-only import and well-structured size mapping.


11-35: LGTM!

Excellent JSDoc documentation with multiple @example tags covering the main use cases. This follows the coding guidelines well.


37-44: LGTM!

Correct use of function component (not arrow function), named export, Readonly<> wrapper for props, and destructuring with sensible defaults.

src/components/atoms/Avatar/Avatar.stories.tsx (3)

1-11: LGTM!

Clean meta setup with satisfies Meta<typeof Avatar> pattern and proper type alias for stories.


13-46: Good story coverage with JSDoc descriptions.

The stories cover key scenarios: default with fallback, image rendering, and broken image handling. Note that the BrokenImage story will only display the fallback correctly once the onError handler is added to the Avatar component.


48-61: LGTM!

The Sizes story effectively demonstrates all size variants in a single view. The render override is appropriate here for displaying multiple component instances.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

## Description
Updated the `Avatar` component to gracefully handle image loading failures. Previously, a broken URL would result in a broken image icon. The component now tracks the image loading state and automatically renders the fallback content if the image fails to load. Additionally, removed verbose JSDoc comments to simplify the file.

## Type of Change
- [x] New feature
- [ ] Bug fix
- [x] Refactoring
- [ ] Documentation update
- [ ] Test addition/update

## Changes Made
- Imported `useState` to manage image error state.
- Added `onError` event handler to the `<img>` element to detect loading failures.
- Updated rendering logic to display the image only if `src` is present and no error has occurred.
- Removed the JSDoc block for the `Avatar` function.
- Removed inline comment regarding background color.

## Testing Done
- [x] All tests pass
- [ ] Added new tests
- [x] Manual testing completed

## Screenshots (if applicable)
N/A

## Related Issues
N/A

## Checklist
- [x] Code follows project style guidelines
- [x] Self-review completed
- [x] No console.log or debugging code in production logic
- [x] All tests passing
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/components/atoms/Avatar/Avatar.tsx`:
- Around line 21-25: The component's stale error state causes the fallback to
remain after src changes; in the Avatar component reset hasError when src
changes by adding an effect that calls setHasError(false) whenever the src prop
updates so showImage (src && !hasError) can become true for the new URL; update
the Avatar component to watch src and clear the error state (referencing
hasError, setHasError, src, showImage).
🧹 Nitpick comments (3)
src/components/atoms/Avatar/Avatar.tsx (3)

12-19: Add JSDoc documentation with @example tag.

Per coding guidelines, components should include JSDoc comments with @example tags for documentation.

📝 Suggested JSDoc
+/**
+ * Avatar component displays a user profile image or styled fallback initials.
+ *
+ * `@example`
+ * // With image
+ * <Avatar src="/user.jpg" alt="Jane Doe" size="md" />
+ *
+ * `@example`
+ * // With fallback initials
+ * <Avatar fallback="JD" size="lg" />
+ */
 export function Avatar(props: Readonly<AvatarProps>) {

37-40: Prefer TailwindCSS classes over inline styles.

Per coding guidelines, TailwindCSS v4 utility classes should be used exclusively for styling. The inline styles here can be replaced with Tailwind classes.

♻️ Suggested refactor for image styles
             <img
                 src={src}
                 alt={alt}
                 width={pixelSize}
                 height={pixelSize}
-                className={className}
+                className={[
+                    "rounded-full object-cover",
+                    className,
+                ].filter(Boolean).join(" ")}
                 onError={() => setHasError(true)}
-                style={{
-                    borderRadius: "50%",
-                    objectFit: "cover",
-                }}
             />

46-65: Refactor fallback to use TailwindCSS and improve accessibility.

Two suggestions:

  1. TailwindCSS: Replace inline styles with Tailwind classes per coding guidelines. Consider a sizeClasses map for width/height.

  2. Accessibility: Add role="img" to pair with aria-label for proper screen reader semantics.

♻️ Suggested refactor
+const sizeClasses: Record<AvatarSize, string> = {
+    xs: "w-6 h-6 text-xs",
+    sm: "w-8 h-8 text-sm",
+    md: "w-10 h-10 text-base",
+    lg: "w-14 h-14 text-lg",
+    xl: "w-[72px] h-[72px] text-xl",
+};

 // FALLBACK RENDER
 return (
     <div
+        role="img"
         aria-label={alt}
-        className={className}
-        style={{
-            width: pixelSize,
-            height: pixelSize,
-            borderRadius: "50%",
-            display: "flex",
-            alignItems: "center",
-            justifyContent: "center",
-            backgroundColor: "#e5e7eb",
-            color: "#374151",
-            fontWeight: 600,
-            fontSize: Math.floor(pixelSize / 2.5),
-            userSelect: "none",
-        }}
+        className={[
+            "rounded-full flex items-center justify-center",
+            "bg-gray-200 text-gray-700 font-semibold select-none",
+            sizeClasses[size],
+            className,
+        ].filter(Boolean).join(" ")}
     >
         {fallback?.slice(0, 2).toUpperCase() ?? "?"}
     </div>
 );
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f5f704 and c95b630.

📒 Files selected for processing (1)
  • src/components/atoms/Avatar/Avatar.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use TypeScript for all files
Prefer type for unions and primitives, interface for object shapes
Use ?. optional chaining and ?? nullish coalescing

Files:

  • src/components/atoms/Avatar/Avatar.tsx
src/components/**/[!.]*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Define interfaces for all component props in separate [ComponentName]Props.ts files

Files:

  • src/components/atoms/Avatar/Avatar.tsx
src/components/**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/components/**/*.tsx: Use function components (not arrow functions for exported components). Export named components: export function ComponentName() {}
Use destructuring for props, but keep the full props object for clarity in React components
Use React 19 features when appropriate
Use JSDoc comments for component documentation with @example tags
Use TailwindCSS v4 utility classes exclusively for styling
Use DaisyUI component classes (e.g., btn, card, input) and variants (e.g., btn-primary, btn-secondary, btn-sm, btn-md, btn-lg)
Build dynamic class names using arrays and .join(' ')
Component files: PascalCase (e.g., Button.tsx, UserProfile.tsx)
Follow established component directory structure: /src/components/[category]/[ComponentName]/ with ComponentName.tsx, ComponentNameProps.ts, ComponentName.test.tsx, ComponentName.stories.tsx files

Files:

  • src/components/atoms/Avatar/Avatar.tsx
🧠 Learnings (3)
📚 Learning: 2025-12-23T08:13:56.218Z
Learnt from: CR
Repo: dbc2201/class-notes PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T08:13:56.218Z
Learning: Applies to src/components/**/*.tsx : Component files: PascalCase (e.g., `Button.tsx`, `UserProfile.tsx`)

Applied to files:

  • src/components/atoms/Avatar/Avatar.tsx
📚 Learning: 2025-12-23T08:13:56.218Z
Learnt from: CR
Repo: dbc2201/class-notes PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T08:13:56.218Z
Learning: Applies to src/components/**/*.tsx : Use function components (not arrow functions for exported components). Export named components: `export function ComponentName() {}`

Applied to files:

  • src/components/atoms/Avatar/Avatar.tsx
📚 Learning: 2025-11-14T06:41:40.575Z
Learnt from: Arnab-Mandal1
Repo: dbc2201/class-notes PR: 39
File: src/components/ui/Input/Input.stories.tsx:25-79
Timestamp: 2025-11-14T06:41:40.575Z
Learning: For the Input component in src/components/ui/Input/, do not flag type issues in the Input.stories.tsx file related to the value prop being set to `{}` or other non-string/number values, as there is runtime protection in the component itself.

Applied to files:

  • src/components/atoms/Avatar/Avatar.tsx
🧬 Code graph analysis (1)
src/components/atoms/Avatar/Avatar.tsx (1)
src/components/atoms/Avatar/AvatarProps.ts (2)
  • AvatarSize (1-1)
  • AvatarProps (3-9)
🔇 Additional comments (1)
src/components/atoms/Avatar/Avatar.tsx (1)

1-2: LGTM!

Imports are appropriate — useState for error state management and type-only import for props.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

## Description
Updated the `Avatar` component to correctly reset its error state when the `src` prop changes, ensuring that new images are attempted even after a previous failure. Enhanced the `onError` handler to prevent infinite loops and refined the JSDoc documentation for better readability.

## Type of Change
- [ ] New feature
- [x] Bug fix
- [x] Refactoring
- [x] Documentation update
- [ ] Test addition/update

## Changes Made
- Added `useEffect` to reset `hasError` to `false` when `src` changes
- Updated `onError` to set `e.currentTarget.onerror = null` to prevent infinite loops
- Changed `src` check to `Boolean(src)` for explicit type coercion
- Simplified and clarified JSDoc comments and examples

## Testing Done
- [x] All tests pass
- [ ] Added new tests
- [x] Manual testing completed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants