# User · HeroUI

> Migration guide for User from HeroUI v2 to v3

- Page: https://jiguang132.com/systems/heroui/docs/migration/user
- Based on: HeroUI documentation (Apache-2.0) @ 60edd8ed751b1d2a270623fbb182bbcfa8baa9af

> 
  The User component has been **removed** in HeroUI v3. Compose user displays manually using Avatar and text elements with Tailwind CSS classes.


## Key Changes

### 1. Component Removal

**v2:** `` component from `@heroui/react`  
**v3:** Manual composition using `Avatar` + text elements

### 2. Features Mapping

The v2 User component had the following features that need to be replaced:

| v2 Feature | v3 Equivalent | Notes |
|-----------|---------------|-------|
| `name` prop | Text element | Render name as text or heading |
| `description` prop | Text element | Render description as text |
| `avatarProps` prop | `Avatar` component | Use v3 Avatar component directly |
| `isFocusable` prop | Manual focus handling | Add `tabIndex` and focus styles if needed |
| `classNames` prop | Tailwind classes | Apply classes directly to elements |

## Structure Changes

### v2: User Component

In v2, `User` was a convenience component combining Avatar with name:

```tsx
import { User } from "@heroui/react";

export default function App() {
  return (
    <User
      name="Junior Garcia"
      avatarProps={{
        src: "https://example.com/avatar.jpg",
      }}
    />
  );
}
```

### v3: Manual Composition

In v3, compose user displays manually using Avatar and text elements:

```tsx
import { Avatar } from "@heroui/react";

export default function App() {
  return (
    <div className="inline-flex items-center gap-2">
      <Avatar>
        <Avatar.Image
          src="https://example.com/avatar.jpg"
          alt="Junior Garcia"
        />
        <Avatar.Fallback>JG</Avatar.Fallback>
      </Avatar>
      <span className="text-sm">Junior Garcia</span>
    </div>
  );
}
```

## Migration Examples

### With Description


  

#### v2


    ```tsx
    import { User } from "@heroui/react";

    <User
      name="Junior Garcia"
      description="Software Engineer"
      avatarProps={{
        src: "https://example.com/avatar.jpg",
      }}
    />
    ```
  
  

#### v3


    ```tsx
    import { Avatar } from "@heroui/react";

    <div className="inline-flex items-center gap-2">
      <Avatar>
        <Avatar.Image
          src="https://example.com/avatar.jpg"
          alt="Junior Garcia"
        />
        <Avatar.Fallback>JG</Avatar.Fallback>
      </Avatar>
      <div className="flex flex-col items-start">
        <span className="text-sm">Junior Garcia</span>
        <span className="text-xs text-muted">Software Engineer</span>
      </div>
    </div>
    ```
  


### With Default Avatar (Initials)


  

#### v2


    ```tsx
    import { User } from "@heroui/react";

    <User
      name="Junior Garcia"
      avatarProps={{
        name: "Junior Garcia",
        getInitials: (name) =>
          name
            .split(" ")
            .map((n) => n[0])
            .join(""),
      }}
    />
    ```
  
  

#### v3


    ```tsx
    import { Avatar } from "@heroui/react";

    function getInitials(name: string) {
      return name
        .split(" ")
        .map((n) => n[0])
        .join("");
    }

    <div className="inline-flex items-center gap-2">
      <Avatar>
        <Avatar.Fallback>{getInitials("Junior Garcia")}</Avatar.Fallback>
      </Avatar>
      <span className="text-sm">Junior Garcia</span>
    </div>
    ```
  


### With Link Description


  

#### v2


    ```tsx
    import { User, Link } from "@heroui/react";

    <User
      name="Junior Garcia"
      description={
        <Link href="https://x.com/jrgarciadev" size="sm">
          @jrgarciadev
        </Link>
      }
      avatarProps={{
        src: "https://example.com/avatar.jpg",
      }}
    />
    ```
  
  

#### v3


    ```tsx
    import { Avatar, Link } from "@heroui/react";

    <div className="inline-flex items-center gap-2">
      <Avatar>
        <Avatar.Image
          src="https://example.com/avatar.jpg"
          alt="Junior Garcia"
        />
        <Avatar.Fallback>JG</Avatar.Fallback>
      </Avatar>
      <div className="flex flex-col items-start">
        <span className="text-sm">Junior Garcia</span>
        <Link href="https://x.com/jrgarciadev" className="text-xs">
          @jrgarciadev
        </Link>
      </div>
    </div>
    ```
  


### Clickable User


  

#### v2


    ```tsx
    import { User } from "@heroui/react";

    {/* Focusable */}
    <User
      name="Junior Garcia"
      isFocusable
      avatarProps={{
        src: "https://example.com/avatar.jpg",
      }}
    />
    
    {/* As button */}
    <User
      as="button"
      name="Junior Garcia"
      avatarProps={{
        src: "https://example.com/avatar.jpg",
      }}
    />
    ```
  
  

#### v3


    ```tsx
    import { Avatar } from "@heroui/react";

    {/* Focusable */}
    <button
      className="inline-flex items-center gap-2 rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-focus"
      tabIndex={0}
    >
      <Avatar>
        <Avatar.Image
          src="https://example.com/avatar.jpg"
          alt="Junior Garcia"
        />
        <Avatar.Fallback>JG</Avatar.Fallback>
      </Avatar>
      <span className="text-sm">Junior Garcia</span>
    </button>
    
    {/* As button */}
    <button
      className="inline-flex items-center gap-2 rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-focus"
    >
      <Avatar>
        <Avatar.Image
          src="https://example.com/avatar.jpg"
          alt="Junior Garcia"
        />
        <Avatar.Fallback>JG</Avatar.Fallback>
      </Avatar>
      <span className="text-sm">Junior Garcia</span>
    </button>
    ```
  


## Creating a Reusable User Component (Recommended)

Since User displays are commonly needed, here's a reusable component:


  

#### v2


    ```tsx
    import { User } from "@heroui/react";

    <User
      name="Junior Garcia"
      description="Software Engineer"
      avatarProps={{
        src: "https://example.com/avatar.jpg",
      }}
    />
    ```
  
  

#### v3


    ```tsx
    import { Avatar, Link } from "@heroui/react";
    import { ReactNode } from "react";
    import { cn } from "@/lib/utils"; // or your cn utility

    interface UserProps {
      name: string | ReactNode;
      description?: string | ReactNode;
      avatarSrc?: string;
      avatarAlt?: string;
      avatarFallback?: string;
      className?: string;
      isFocusable?: boolean;
      as?: "div" | "button" | "a";
      href?: string;
      onClick?: () => void;
    }

    function getInitials(name: string): string {
      return name
        .split(" ")
        .map((n) => n[0])
        .join("")
        .toUpperCase()
        .slice(0, 2);
    }

    export function User({
      name,
      description,
      avatarSrc,
      avatarAlt,
      avatarFallback,
      className,
      isFocusable = false,
      as = "div",
      href,
      onClick,
    }: UserProps) {
      const Component = as === "a" ? "a" : as === "button" ? "button" : "div";
      const fallback = avatarFallback || (typeof name === "string" ? getInitials(name) : "?");

      const content = (
        <>
          <Avatar>
            {avatarSrc && (
              <Avatar.Image
                src={avatarSrc}
                alt={avatarAlt || (typeof name === "string" ? name : "")}
              />
            )}
            <Avatar.Fallback>{fallback}</Avatar.Fallback>
          </Avatar>
          <div className="flex flex-col items-start">
            <span className="text-sm">{name}</span>
            {description && (
              <span className="text-xs text-muted">{description}</span>
            )}
          </div>
        </>
      );

      const baseClasses = cn(
        "inline-flex items-center gap-2 rounded-sm outline-none",
        isFocusable && "focus-visible:ring-2 focus-visible:ring-focus",
        className
      );

      if (Component === "button") {
        return (
          <button className={baseClasses} onClick={onClick} tabIndex={isFocusable ? 0 : -1}>
            {content}
          </button>
        );
      }

      if (Component === "a") {
        return (
          <a href={href} className={baseClasses} tabIndex={isFocusable ? 0 : -1}>
            {content}
          </a>
        );
      }

      return (
        <div className={baseClasses} tabIndex={isFocusable ? 0 : -1}>
          {content}
        </div>
      );
    }

    // Usage
    <User
      name="Junior Garcia"
      description="Software Engineer"
      avatarSrc="https://example.com/avatar.jpg"
      avatarAlt="Junior Garcia"
    />
    ```
  


## Styling Reference

The v2 User component used these base styles that you should replicate:

- **Base container**: `inline-flex items-center gap-2 rounded-sm`
- **Wrapper (for name/description)**: `inline-flex flex-col items-start`
- **Name**: `text-sm` (text-small)
- **Description**: `text-xs text-muted` (text-tiny text-foreground-400)

## Summary

1. **Component Removed**: `User` component no longer exists in v3
2. **Import Change**: Remove `import { User } from "@heroui/react"`
3. **Manual Composition**: Compose using Avatar + text elements
4. **Avatar Changes**: Use v3 Avatar compound component pattern
5. **Styling**: Apply Tailwind CSS classes directly
6. **Focus Handling**: Implement focus styles manually if needed

## Migration Steps

1. **Remove Import**: Remove `User` from `@heroui/react` imports
2. **Replace Component**: Replace all `` instances with manual composition
3. **Use Avatar**: Use v3 Avatar component with compound pattern
4. **Add Text Elements**: Add name and description as text elements
5. **Apply Styling**: Use Tailwind CSS classes for layout and styling
6. **Handle Focus**: Add focus styles if `isFocusable` was used
7. **Optional**: Create reusable User component for your application

## Common Patterns

### User List

```tsx
<div className="space-y-2">
  {users.map((user) => (
    <div key={user.id} className="inline-flex items-center gap-2">
      <Avatar>
        <Avatar.Image src={user.avatar} alt={user.name} />
        <Avatar.Fallback>{getInitials(user.name)}</Avatar.Fallback>
      </Avatar>
      <div className="flex flex-col items-start">
        <span className="text-sm">{user.name}</span>
        {user.role && (
          <span className="text-xs text-muted">{user.role}</span>
        )}
      </div>
    </div>
  ))}
</div>
```

### Clickable User

```tsx
<button
  className="inline-flex items-center gap-2 rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-focus hover:bg-default-100"
  onClick={() => handleUserClick(user)}
>
  <Avatar>
    <Avatar.Image src={user.avatar} alt={user.name} />
    <Avatar.Fallback>{getInitials(user.name)}</Avatar.Fallback>
  </Avatar>
  <div className="flex flex-col items-start">
    <span className="text-sm">{user.name}</span>
    <span className="text-xs text-muted">{user.email}</span>
  </div>
</button>
```

---

Index of all JGuang resources: https://jiguang132.com/llms.txt
