@usevyre/react @usevyre/vue

Conversation

A chat / inbox message thread. Like Kanban, it is fully controlled and data-driven — you own the messages array and append in your own send handler. Consecutive messages from the same author are grouped (avatar & name shown once), day separators are inserted on date change, and your own messages (authorId === currentUserId) align to the right.


Basic

Pass a controlled value array and the currentUserId that decides which messages are outgoing (rendered on the right). Enable the built-in composer and append to your own state in onSend (@send in Vue). Each message may carry a delivery status (sending / sent / delivered / read).

S
Sam Rivera
Hey! Did you see the API draft?
Yep — looks solid.
Read
S
Sam Rivera
It's fully controlled, right?
Exactly — same as Kanban. You own the array.
Delivered
Sam is typing
import { useState } from "react";
import { Conversation } from "@usevyre/react";
import type { ConversationMessage } from "@usevyre/react";

const [messages, setMessages] = useState<ConversationMessage[]>([
  { id: "1", authorId: "sam", authorName: "Sam Rivera", text: "Hey! Did you see the API draft?" },
  { id: "2", authorId: "me", text: "Yep — looks solid.", status: "read" },
  { id: "3", authorId: "sam", authorName: "Sam Rivera", text: "It's fully controlled, right?" },
  { id: "4", authorId: "me", text: "Exactly — same as Kanban. You own the array.", status: "delivered" },
]);

let nextId = 100;

// onSend receives (text, files). Basic chat ignores files —
// see the Attachments example for the full signature.
<Conversation
  value={messages}
  currentUserId="me"
  composer
  typing="Sam is typing"
  onSend={(text) =>
    setMessages((m) => [
      ...m,
      { id: String(++nextId), authorId: "me", text, status: "sent" },
    ])
  }
/>

Custom bubbles

Use renderMessage (React render prop) or the #message scoped slot (Vue) to render any content inside a bubble — badges, rich text, custom layouts. The meta argument tells you whether the message is outgoing and where it sits in its author group.

V
Vyre Bot
bot Pick a component to scaffold:
Kanban please
Read
V
Vyre Bot
bot Done — added Kanban.tsx + Kanban.vue.
import { useState } from "react";
import { Conversation, Badge } from "@usevyre/react";
import type { ConversationMessage } from "@usevyre/react";

const [messages, setMessages] = useState<ConversationMessage[]>([
  { id: "a", authorId: "bot", authorName: "Vyre Bot", text: "Pick a component to scaffold:" },
  { id: "b", authorId: "me", text: "Kanban please", status: "read" },
  { id: "c", authorId: "bot", authorName: "Vyre Bot", text: "Done — added Kanban.tsx + Kanban.vue." },
]);

<Conversation
  value={messages}
  currentUserId="me"
  composer
  placeholder="Ask the bot…"
  onSend={(text) =>
    setMessages((m) => [
      ...m,
      { id: String(Date.now()), authorId: "me", text, status: "sent" },
    ])
  }
  renderMessage={(msg, meta) => (
    <span>
      {!meta.outgoing && <Badge variant="teal">bot</Badge>} {msg.text}
    </span>
  )}
/>

Attachments

Add an attachments array to any message. Each attachment has a kind of image, audio, video, or file — rendered inside the bubble as an image preview, a native audio/video player, or a download chip (with optional size).

To let users attach files from the composer, set allowAttachments — a 📎 button and staged-file chips appear, and onSend (@send in Vue) is called with (text, files) where files is a File[]. You own the upload and turning each File into a message attachment (use accept to restrict file types).

S
Sam Rivera
moodboard.png
Here's the moodboard 👇
Love it. Specs attached.
Read
S
Sam Rivera
const [messages, setMessages] = useState<ConversationMessage[]>([
  {
    id: "1", authorId: "sam", authorName: "Sam Rivera",
    text: "Here's the moodboard 👇",
    attachments: [
      { kind: "image", url: "https://picsum.photos/seed/vyre/320/200", name: "moodboard.png" },
    ],
  },
  {
    id: "2", authorId: "me", text: "Love it. Specs attached.", status: "read",
    attachments: [
      { kind: "file", url: "#", name: "design-spec.pdf", size: "2.4 MB" },
    ],
  },
  {
    id: "3", authorId: "sam", authorName: "Sam Rivera",
    attachments: [
      { kind: "audio", url: "https://www.w3schools.com/html/horse.mp3" },
    ],
  },
]);

<Conversation
  value={messages}
  currentUserId="me"
  composer
  allowAttachments
  onSend={(text, files) =>
    setMessages((m) => [
      ...m,
      {
        id: String(Date.now()),
        authorId: "me",
        text,
        status: "sent",
        attachments: files.map((f) =>
          f.type.startsWith("image/")
            ? { kind: "image", url: URL.createObjectURL(f), name: f.name }
            : { kind: "file", url: URL.createObjectURL(f), name: f.name }
        ),
      },
    ])
  }
/>

Props

Props

Prop Type Default Description
value ConversationMessage[] Ordered messages. ConversationMessage = id, authorId, text?, authorName?, authorAvatar?, timestamp?, status?, attachments?. Required & controlled.
currentUserId string Whose messages are outgoing (aligned right). Matched against message.authorId. Required.
composer boolean false Show the built-in input + Send button. Pair with onSend / @send.
onSend / @send (text: string, files: File[]) => void Called when the composer submits. files holds anything staged via the attach button (empty when allowAttachments is off). You own the upload.
placeholder string "Write a message…" Composer input placeholder.
typing boolean | string false Show an incoming typing indicator. Pass a string to label it.
allowAttachments boolean false Show a 📎 attach button + staged-file chips in the built-in composer.
accept string Forwarded to the file input's accept attribute (e.g. "image/*").
renderMessage / #message (message, meta) => ReactNode Custom bubble body. meta = { outgoing, isGroupStart, isGroupEnd }. Vue: #message scoped slot.
renderComposer / #composer (api) => ReactNode Replace the composer entirely. api = { value, setValue, send }. Vue: #composer scoped slot.
className / class string Additional CSS class on the root element.

ConversationAttachment

Props

Prop Type Default Description
kind "image" | "audio" | "video" | "file" Renders an image preview, an audio/video player, or a download chip for files.
url string Media source (image/audio/video) or download href (file).
name string Image alt text / file display name.
size string Optional human-readable file size, e.g. "2.4 MB" (file kind).