Kosmesis
Components

Avatar

An image with automatic fallback, wrapping Morphos's Avatar/AvatarImage/AvatarFallback.

Installation

npx kosmesis add avatar
pnpm dlx kosmesis add avatar
yarn dlx kosmesis add avatar
bunx kosmesis add avatar

Install the following dependencies:

npm install @morphos/feedback
pnpm add @morphos/feedback
yarn add @morphos/feedback
bun add @morphos/feedback

Copy and paste the following code into your project.

avatar.tsx
import { StatelessComponent } from "@praxisjs/core";import { Component } from "@praxisjs/decorators";import { Avatar as MorphosAvatar, AvatarFallback as MorphosAvatarFallback, AvatarImage as MorphosAvatarImage, type AvatarFallbackProps as MorphosAvatarFallbackProps  } from "@morphos/feedback";import { cn } from "@/lib/utils";/** * Extends (not wraps) Morphos's `Avatar` so `new Avatar()` still yields a real instance with * `.setImageStatus()`/`.imageLoaded`/`.imageError` — what `AvatarImage`/`AvatarFallback` need via * their `avatar` prop. */@Component()export class Avatar extends MorphosAvatar {  render() {    return (      <span id={this.id} class={cn("relative flex size-8 shrink-0 overflow-hidden rounded-full", this.class)} data-status={() => this._imageStatus}>        {this.children}      </span>    );  }}export interface AvatarImageProps {  avatar: Avatar;  src: string;  alt: string;  class?: string;}@Component()export class AvatarImage extends StatelessComponent<AvatarImageProps> {  render() {    const { avatar, src, alt, class: cls } = this.props;    return <MorphosAvatarImage avatar={avatar} src={src} alt={alt} class={cn("aspect-square size-full", cls)} />;  }}@Component()export class AvatarFallback extends StatelessComponent<MorphosAvatarFallbackProps> {  render() {    const { avatar, class: cls, id, children } = this.props;    return (      <MorphosAvatarFallback        avatar={avatar}        id={id}        class={cn("flex size-full items-center justify-center rounded-full bg-muted text-sm font-medium", cls)}      >        {children}      </MorphosAvatarFallback>    );  }}

Install the following dependencies:

npm install @morphos/feedback @praxisjs/css
pnpm add @morphos/feedback @praxisjs/css
yarn add @morphos/feedback @praxisjs/css
bun add @morphos/feedback @praxisjs/css

Copy and paste the following code into your project.

avatar.tsx
import { StatelessComponent } from "@praxisjs/core";import { cx, Stylesheet, Styled, tokenVars } from "@praxisjs/css";import { Component } from "@praxisjs/decorators";import { Avatar as MorphosAvatar, AvatarFallback as MorphosAvatarFallback, AvatarImage as MorphosAvatarImage, type AvatarFallbackProps as MorphosAvatarFallbackProps  } from "@morphos/feedback";import { KosmesisTokens } from "@/lib/kosmesis-theme";const t = tokenVars(KosmesisTokens);class AvatarStyles extends Stylesheet {  $root = this.css({    position: "relative",    display: "flex",    width: "2rem",    height: "2rem",    flexShrink: 0,    overflow: "hidden",    borderRadius: "9999px",  });  $image = this.css({ aspectRatio: "1 / 1", width: "100%", height: "100%" });  $fallback = this.css({    display: "flex",    width: "100%",    height: "100%",    alignItems: "center",    justifyContent: "center",    borderRadius: "9999px",    backgroundColor: t.muted,    fontSize: "0.875rem",    fontWeight: 500,  });}/** * Extends (not wraps) Morphos's `Avatar` so `new Avatar()` still yields a real instance with * `.setImageStatus()`/`.imageLoaded`/`.imageError` — what `AvatarImage`/`AvatarFallback` need via * their `avatar` prop. */@Component()export class Avatar extends MorphosAvatar {  @Styled(AvatarStyles) $s!: AvatarStyles;  render() {    return (      <span id={this.id} class={cx(this.$s.$root, this.class)} data-status={() => this._imageStatus}>        {this.children}      </span>    );  }}export interface AvatarImageProps {  avatar: Avatar;  src: string;  alt: string;  class?: string;}@Component()export class AvatarImage extends StatelessComponent<AvatarImageProps> {  @Styled(AvatarStyles) $s!: AvatarStyles;  render() {    const { avatar, src, alt, class: cls } = this.props;    return <MorphosAvatarImage avatar={avatar} src={src} alt={alt} class={cx(this.$s.$image, cls)} />;  }}@Component()export class AvatarFallback extends StatelessComponent<MorphosAvatarFallbackProps> {  @Styled(AvatarStyles) $s!: AvatarStyles;  render() {    const { avatar, class: cls, id, children } = this.props;    return (      <MorphosAvatarFallback avatar={avatar} id={id} class={cx(this.$s.$fallback, cls)}>        {children}      </MorphosAvatarFallback>    );  }}

Examples

About

Wraps Morphos's Avatar compound component. AvatarImage reports load/error status onto the root's data-status attribute; AvatarFallback reads that status to hide itself once the image has loaded.

Usage

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

@Component()
class MyPage extends StatefulComponent {
  @State() avatar = new Avatar();

  render() {
    return (
      <Avatar>
        <AvatarImage avatar={this.avatar} src="/avatars/01.png" alt="Jane Doe" />
        <AvatarFallback avatar={this.avatar}>JD</AvatarFallback>
      </Avatar>
    );
  }
}

Avatar renders the wrapping <span> itself, but AvatarImage/AvatarFallback still need the same Avatar instance passed via their avatar prop to read/report load status — instantiate it once (@State() avatar = new Avatar()), same as every other Morphos compound component.

Props

Avatar

No own props beyond class/id/children.

AvatarImage

PropTypeDefault
avatarAvatar— required
srcstring— required
altstring— required

AvatarFallback

PropTypeDefault
avatarAvatar— required

On this page