Chatbot

Message

Displays a chat interface message from either a user or an AI.

The Message component displays a chat interface message from either a user or an AI. It includes an avatar, a name, and a message content.

Hello, how are you?
Ha
"use client";import { Message, MessageAvatar, MessageContent } from "@/components/ai-elements/elements/message";import { nanoid } from "nanoid";const messages: {  key: string;  from: "user" | "assistant";  content: string;  avatar: string;  name: string;}[] = [  {    key: nanoid(),    from: "user",    content: "Hello, how are you?",    avatar: "https://github.com/haydenbleasel.png",    name: "Hayden Bleasel",  },];const Example = () => (  <>    {messages.map(({ content, ...message }) => (      <Message from={message.from} key={message.key}>        <MessageContent>{content}</MessageContent>        <MessageAvatar name={message.name} src={message.avatar} />      </Message>    ))}  </>);export default Example;

Installation

npx ai-elements@latest add message
npx shadcn@latest add @ai-elements/message
import {  Avatar,  AvatarFallback,  AvatarImage,} from "@repo/shadcn-ui/components/ui/avatar";import { cn } from "@repo/shadcn-ui/lib/utils";import type { UIMessage } from "ai";import { cva, type VariantProps } from "class-variance-authority";import type { ComponentProps, HTMLAttributes } from "react";export type MessageProps = HTMLAttributes<HTMLDivElement> & {  from: UIMessage["role"];};export const Message = ({ className, from, ...props }: MessageProps) => (  <div    className={cn(      "group flex w-full items-end justify-end gap-2 py-4",      from === "user" ? "is-user" : "is-assistant flex-row-reverse justify-end",      className    )}    {...props}  />);const messageContentVariants = cva(  "is-user:dark flex flex-col gap-2 overflow-hidden rounded-lg text-sm",  {    variants: {      variant: {        contained: [          "max-w-[80%] px-4 py-3",          "group-[.is-user]:bg-primary group-[.is-user]:text-primary-foreground",          "group-[.is-assistant]:bg-secondary group-[.is-assistant]:text-foreground",        ],        flat: [          "group-[.is-user]:max-w-[80%] group-[.is-user]:bg-secondary group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:text-foreground",          "group-[.is-assistant]:text-foreground",        ],      },    },    defaultVariants: {      variant: "contained",    },  });export type MessageContentProps = HTMLAttributes<HTMLDivElement> &  VariantProps<typeof messageContentVariants>;export const MessageContent = ({  children,  className,  variant,  ...props}: MessageContentProps) => (  <div    className={cn(messageContentVariants({ variant, className }))}    {...props}  >    {children}  </div>);export type MessageAvatarProps = ComponentProps<typeof Avatar> & {  src: string;  name?: string;};export const MessageAvatar = ({  src,  name,  className,  ...props}: MessageAvatarProps) => (  <Avatar className={cn("size-8 ring-1 ring-border", className)} {...props}>    <AvatarImage alt="" className="mt-0 mb-0" src={src} />    <AvatarFallback>{name?.slice(0, 2) || "ME"}</AvatarFallback>  </Avatar>);

Usage

import { Message, MessageContent } from '@/components/ai-elements/message';
// Default contained variant
<Message from="user">
  <MessageContent>Hi there!</MessageContent>
</Message>

// Flat variant for a minimalist look
<Message from="assistant">
  <MessageContent variant="flat">Hello! How can I help you today?</MessageContent>
</Message>

Usage with AI SDK

Render messages in a list with useChat.

Add the following component to your frontend:

app/page.tsx
'use client';

import { Message, MessageContent } from '@/components/ai-elements/message';
import { useChat } from '@ai-sdk/react';
import { Response } from '@/components/ai-elements/response';

const MessageDemo = () => {
  const { messages } = useChat();

  return (
    <div className="max-w-4xl mx-auto p-6 relative size-full rounded-lg border h-[600px]">
      <div className="flex flex-col h-full">
        {messages.map((message) => (
          <Message from={message.role} key={message.id}>
            <MessageContent>
              {message.parts.map((part, i) => {
                switch (part.type) {
                  case 'text': // we don't use any reasoning or tool calls in this example
                    return (
                      <Response key={`${message.id}-${i}`}>
                        {part.text}
                      </Response>
                    );
                  default:
                    return null;
                }
              })}
            </MessageContent>
          </Message>
        ))}
      </div>
    </div>
  );
};

export default MessageDemo;

Features

  • Displays messages from both the user and AI assistant with distinct styling.
  • Two visual variants: contained (default) and flat for different design preferences.
  • Includes avatar images for message senders with fallback initials.
  • Shows the sender's name through avatar fallbacks.
  • Automatically aligns user and assistant messages on opposite sides.
  • Uses different background colors for user and assistant messages.
  • Accepts any React node as message content.

Variants

Contained (default)

The contained variant provides distinct visual separation with colored backgrounds:

  • User messages appear with primary background color and are right-aligned
  • Assistant messages have secondary background color and are left-aligned
  • Both message types have padding and rounded corners

Flat

The flat variant offers a minimalist design that matches modern AI interfaces like ChatGPT and Gemini:

  • User messages use softer secondary colors with subtle borders
  • Assistant messages display full-width without background or padding
  • Creates a cleaner, more streamlined conversation appearance

Notes

Always render the AIMessageContent first, then the AIMessageAvatar. The AIMessage component is a wrapper that determines the alignment of the message.

Examples

Render Markdown

We can use the Response component to render markdown content.

What is the weather in Tokyo?

Ha
Op
"use client";import { Message, MessageAvatar, MessageContent } from "@/components/ai-elements/elements/message";import { Response } from "@/components/ai-elements/elements/response";import { useEffect, useState } from "react";const assistantMessageTokens = [  "To",  " get",  " the",  " **",  "weather",  " in",  " Tokyo",  "**",  " using",  " an",  " API",  " call",  ",",  " you",  " can",  " use",  " the",  " [",  "OpenWeatherMap",  "](",  "https://openweathermap.org/api",  ")",  " API",  ".",  " After",  " signing",  " up",  ",",  " you",  " can",  " make",  " a",  " request",  " to",  " their",  " API",  ":",  "\n\n",  "```",  "bash",  "\n",  "curl",  " -X",  " GET",  ' "https://api.openweathermap.org/data/2.5/weather?q=Tokyo&appid=YOUR_API_KEY"',  " \\",  "\n",  "  --header",  ' "Content-Type:',  ' application/json"',  " \\",  "\n",  "  --header",  ' "Accept:',  ' application/json"',  "\n",  "```",  "\n\n",  "This",  " will",  " return",  " a",  " JSON",  " object",  " with",  " the",  " weather",  " data",  " for",  " Tokyo",  ".",];const Example = () => {  const [content, setContent] = useState("");  useEffect(() => {    let currentContent = "";    let index = 0;    const interval = setInterval(() => {      if (index < assistantMessageTokens.length) {        currentContent += assistantMessageTokens[index];        setContent(currentContent);        index++;      } else {        clearInterval(interval);      }    }, 100);    return () => clearInterval(interval);  }, []);  return (    <>      <Message from="user">        <MessageContent>          <Response>What is the weather in Tokyo?</Response>        </MessageContent>        <MessageAvatar          name="Hayden Bleasel"          src="https://github.com/haydenbleasel.png"        />      </Message>      <Message from="assistant">        <MessageContent>          <Response>{content}</Response>        </MessageContent>        <MessageAvatar name="OpenAI" src="https://github.com/openai.png" />      </Message>    </>  );};export default Example;

Flat Variant

The flat variant provides a minimalist design that matches modern AI interfaces.

Can you explain what the flat variant does?
The flat variant provides a minimalist design for messages. User messages appear with subtle secondary colors and borders, while assistant messages display full-width without background padding. This creates a cleaner, more modern conversation interface similar to ChatGPT and other contemporary AI assistants.
That looks much cleaner! I like how it matches modern AI interfaces.
Exactly! The flat variant is perfect when you want a more streamlined appearance that puts focus on the conversation content rather than visual containers. It works especially well in full-width layouts and professional applications.
"use client";import { Message, MessageContent } from "@/components/ai-elements/elements/message";import { nanoid } from "nanoid";const messages: {  key: string;  from: "user" | "assistant";  content: string;  avatar: string;  name: string;}[] = [  {    key: nanoid(),    from: "user",    content: "Can you explain what the flat variant does?",    avatar: "https://github.com/haydenbleasel.png",    name: "Hayden Bleasel",  },  {    key: nanoid(),    from: "assistant",    content:      "The flat variant provides a minimalist design for messages. User messages appear with subtle secondary colors and borders, while assistant messages display full-width without background padding. This creates a cleaner, more modern conversation interface similar to ChatGPT and other contemporary AI assistants.",    avatar: "https://github.com/anthropics.png",    name: "Claude",  },  {    key: nanoid(),    from: "user",    content:      "That looks much cleaner! I like how it matches modern AI interfaces.",    avatar: "https://github.com/haydenbleasel.png",    name: "Hayden Bleasel",  },  {    key: nanoid(),    from: "assistant",    content:      "Exactly! The flat variant is perfect when you want a more streamlined appearance that puts focus on the conversation content rather than visual containers. It works especially well in full-width layouts and professional applications.",    avatar: "https://github.com/anthropics.png",    name: "Claude",  },];const Example = () => (  <div className="space-y-2">    {messages.map(({ content, ...message }) => (      <Message from={message.from} key={message.key}>        <MessageContent variant="flat">{content}</MessageContent>      </Message>    ))}  </div>);export default Example;

Props

<Message />

Prop

Type

<MessageContent />

Prop

Type

<MessageAvatar />

Prop

Type