Kosmesis
Components

Message

Composes Bubble with an optional avatar and role-based layout for chat UIs.

Installation

npx kosmesis add message
pnpm dlx kosmesis add message
yarn dlx kosmesis add message
bunx kosmesis add message

This component builds on other Kosmesis components — install these first (via kosmesis add or this same manual process on their own pages): bubble.

Copy and paste the following code into your project.

message.tsx
import { StatelessComponent } from "@praxisjs/core";import { Component } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { Bubble } from "./bubble";import { cn } from "@/lib/utils";export interface MessageProps {  from: "user" | "assistant";  class?: string;  /** Rendered before the bubble (e.g. an `Avatar`). */  avatar?: Children;  children?: Children;}/** * Composes `Bubble` with an optional avatar and role-based layout — no Morphos equivalent. * `from="user"` mirrors the row and uses the "sent" bubble variant; `from="assistant"` keeps * normal reading order with the "received" variant. */@Component()export class Message extends StatelessComponent<MessageProps> {  render() {    const { from, class: cls, avatar, children } = this.props;    return (      <div class={cn("flex items-end gap-2", from === "user" && "flex-row-reverse", cls)}>        {avatar}        <Bubble variant={from === "user" ? "sent" : "received"}>{children}</Bubble>      </div>    );  }}export interface MessageGroupProps {  class?: string;  children?: Children;}@Component()export class MessageGroup extends StatelessComponent<MessageGroupProps> {  render() {    const { class: cls, children } = this.props;    return <div class={cn("flex flex-col gap-3 p-4", cls)}>{children}</div>;  }}

Install the following dependencies:

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

This component builds on other Kosmesis components — install these first (via kosmesis add or this same manual process on their own pages): bubble.

Copy and paste the following code into your project.

message.tsx
import { StatelessComponent } from "@praxisjs/core";import { cx, Stylesheet, Styled } from "@praxisjs/css";import { Component } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { Bubble } from "./bubble";class MessageStyles extends Stylesheet {  $root = this.css({ display: "flex", alignItems: "flex-end", gap: "0.5rem" });  $reversed = this.css({ flexDirection: "row-reverse" });  $group = this.css({ display: "flex", flexDirection: "column", gap: "0.75rem", padding: "1rem" });}export interface MessageProps {  from: "user" | "assistant";  class?: string;  /** Rendered before the bubble (e.g. an `Avatar`). */  avatar?: Children;  children?: Children;}/** * Composes `Bubble` with an optional avatar and role-based layout — no Morphos equivalent. * `from="user"` mirrors the row and uses the "sent" bubble variant; `from="assistant"` keeps * normal reading order with the "received" variant. */@Component()export class Message extends StatelessComponent<MessageProps> {  @Styled(MessageStyles) $s!: MessageStyles;  render() {    const { from, class: cls, avatar, children } = this.props;    return (      <div class={cx(this.$s.$root, from === "user" && this.$s.$reversed, cls)}>        {avatar}        <Bubble variant={from === "user" ? "sent" : "received"}>{children}</Bubble>      </div>    );  }}export interface MessageGroupProps {  class?: string;  children?: Children;}@Component()export class MessageGroup extends StatelessComponent<MessageGroupProps> {  @Styled(MessageStyles) $s!: MessageStyles;  render() {    const { class: cls, children } = this.props;    return <div class={cx(this.$s.$group, cls)}>{children}</div>;  }}

Examples

Usage

import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Message, MessageGroup } from "@/components/ui/message";

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

  render() {
    return (
      <MessageGroup>
        <Message from="assistant" avatar={
          <Avatar class="size-6"><AvatarFallback avatar={this.avatar}>AI</AvatarFallback></Avatar>
        }>
          How can I help you today?
        </Message>
        <Message from="user">What's the weather like?</Message>
      </MessageGroup>
    );
  }
}

Use with Message Scroller for a scrollable chat viewport, and Attachment for file previews within a message.

On this page