Avatar

An image element with a fallback for representing a user.
CU
Installation
Expand

npx @left4code/cosmic-ui-cli@latest add avatar
 
Expand

This writes the component and everything it imports, then installs the packages it needs. Run npx @left4code/cosmic-ui-cli@latest initfirst if you haven't already.

Install the following dependencies:

pnpm
npm
yarn
bun
yarn add @zag-js/avatar @zag-js/react

Built directly on the Zag.js state machine. The fallback and image both render unconditionally - the machine toggles a hidden attribute on whichever one shouldn't show, based on whether the image loaded. The square border shown here is a placeholder; swap it for the Frame primitive once its avatar-specific paths exist. Copy and paste the following code into your project.

components/ui/avatar.tsx
Expand

import { createContext, useContext, useId } from "react";
import { twMerge } from "tailwind-merge";
import { useMachine, normalizeProps } from "@zag-js/react";
import * as avatarMachine from "@zag-js/avatar";

const AvatarContext = createContext<ReturnType<typeof avatarMachine.connect> | null>(
  null
);

function useAvatarContext() {
  const api = useContext(AvatarContext);
  if (!api) throw new Error("Avatar parts must be used within <AvatarRoot>");
  return api;
}

function AvatarRoot({
  children,
  className,
  ...rest
}: React.PropsWithChildren<Partial<avatarMachine.Props> & { className?: string }>) {
  const service = useMachine(avatarMachine.machine, { id: useId(), ...rest });
  const api = avatarMachine.connect(service, normalizeProps);

  return (
    <AvatarContext.Provider value={api}>
      <span
        {...api.getRootProps()}
        className={twMerge([
          "relative inline-flex size-10 items-center justify-center overflow-hidden border border-primary/30 bg-primary/10",
          className,
        ])}
      >
        {children}
      </span>
    </AvatarContext.Provider>
  );
}

function AvatarImage({
  src,
  alt,
  className,
}: {
  src: string;
  alt: string;
  className?: string;
}) {
  const api = useAvatarContext();

  return (
    <img
      {...api.getImageProps()}
      src={src}
      alt={alt}
      className={twMerge(["size-full object-cover", className])}
    />
  );
}

function AvatarFallback({
  children,
  className,
}: React.PropsWithChildren<{ className?: string }>) {
  const api = useAvatarContext();

  return (
    <span
      {...api.getFallbackProps()}
      className={twMerge(["font-medium", className])}
    >
      {children}
    </span>
  );
}

export { AvatarRoot, AvatarImage, AvatarFallback };
 
Expand

Update the import paths to match your project setup.

Usage
Expand

import { AvatarRoot, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
 
Expand
Expand

<AvatarRoot>
  <AvatarImage src="https://i.pravatar.cc/150?u=cosmic-ui" alt="User avatar" />
  <AvatarFallback>CU</AvatarFallback>
</AvatarRoot>
 
Expand
Powered by synthetic caffeine · Deployed by Left4code · Signal traceable on GitHub.