← All Skills

CrayonChat

v1.0.1 UI
7 files, 39.1 KB ~1,661 words · 7 min read Updated 2026-03-26

AI chat UI framework — streaming, context management, theming. Drop-in React components.

$ npx snappy-skills install crayonchat
zip ↓
Documents
SKILL.md
6.2 KB

CrayonChat Integration Guide#

Purpose#

Battle-tested patterns for building AI chat interfaces with @crayonai/react-core and @crayonai/react-ui.

When to Use This Skill#

Activates when:

  • Working with CrayonChat, @crayonai/react-core, or @crayonai/react-ui
  • Building AI chat interfaces with SSE streaming
  • Creating custom response templates for chat
  • Managing conversation threads with useThreadManager
  • Implementing processMessage callbacks

Quick Start#

tsximport { useThreadManager, processStreamedMessage } from "@crayonai/react-core";
import { CrayonChat } from "@crayonai/react-ui";
import "@crayonai/react-ui/styles/index.css";

const templates = [
  { name: "my_card", Component: MyCard },
];

function Chat() {
  const threadManager = useThreadManager({
    threadId: null,
    loadThread: async (id) => [],
    onProcessMessage: async ({ message, threadManager: tm, abortController }) => {
      const response = await myProcessMessage({
        threadId: "new",
        messages: tm.messages.concat({ ...message, id: crypto.randomUUID() }),
        abortController,
      });
      await processStreamedMessage({
        response,
        createMessage: tm.appendMessages,
        updateMessage: tm.updateMessage,
        deleteMessage: tm.deleteMessage,
      });
      return [];
    },
    responseTemplates: templates,
  });

  return (
    <CrayonChat
      type="standalone"
      threadManager={threadManager}
      responseTemplates={templates}
      agentName="Assistant"
      logoUrl="/logo.svg"
      scrollVariant="always"
      messageLoadingComponent={() => <MyLoader />}
      welcomeMessage={{ title: "Hello", description: "How can I help?" }}
      conversationStarters={{
        variant: "long",
        options: [{ displayText: "Get started", prompt: "Help me get started" }],
      }}
    />
  );
}

Need to... Read this
Understand CrayonChat props component-api.md
Build SSE streaming sse-streaming.md
Create custom templates templates.md
Manage threads & state thread-management.md
Style & theme the chat styling.md
Avoid common pitfalls gotchas.md

Core Architecture#

CrayonChat (UI shell)
  ├── threadManager (state + actions)
  │   ├── messages[]  ← UserMessage | AssistantMessage
  │   ├── isRunning   ← true while streaming
  │   └── processMessage() → SSE Response → processStreamedMessage()
  ├── responseTemplates[] ← { name, Component }
  │   └── Templates use useThreadActions() / useThreadState()
  └── SSE stream format:
      event: text\ndata: word\n\n        ← streamed text
      event: tpl\ndata: {name, templateProps}\n\n  ← template card

Key Types (from @crayonai/react-core)#

typescript// Messages
type UserMessage = { id: string; role: "user"; type: "prompt"; message?: string; context?: JSONValue[] };
type AssistantMessage = {
  id: string; role: "assistant"; context?: JSONValue[];
  message?: ({ type: "text"; text: string } | { type: "template"; name: string; templateProps: any })[];
};
type Message = UserMessage | AssistantMessage;
type CreateMessage = Omit<UserMessage, "id">;

// Templates
interface ResponseTemplate { name: string; Component: React.ComponentType<any> }

// Thread state
type ThreadState = {
  isRunning?: boolean;
  isLoadingMessages?: boolean;
  messages: Message[];
  error: Error | null | undefined;
  responseTemplates: Record<string, ResponseTemplate>;
  isInitialized: boolean;
};

// Thread actions
type ThreadActions = {
  processMessage: (message: CreateMessage) => Promise<void>;
  appendMessages: (...messages: Message[]) => void;
  updateMessage: (message: Message) => void;
  deleteMessage: (messageId: string) => void;
  onCancel: () => void;
  setMessages: (messages: Message[]) => void;
};

Quick Reference#

Imports:

typescript// Core
import { useThreadManager, useThreadState, useThreadActions, processStreamedMessage } from "@crayonai/react-core";
import type { Message, UserMessage, AssistantMessage, CreateMessage, ResponseTemplate, ThreadManager } from "@crayonai/react-core";

// UI
import { CrayonChat, CheckBoxGroup, CheckBoxItem } from "@crayonai/react-ui";
import "@crayonai/react-ui/styles/index.css";

CrayonChat types: "standalone" | "copilot" | "bottom-tray"

Scroll variants: "always" (auto-scroll) is the safe default

Template component hooks:

  • useThreadActions(){ processMessage } — send follow-up messages from cards
  • useThreadState(){ isRunning, messages } — read current state in templates

Resource Files#

component-api.md#

Complete CrayonChat component props, welcome message config, conversation starters

sse-streaming.md#

SSE stream format, processMessage callback, building Response objects, text chunking

templates.md#

Template registry, creating custom templates, template props, interactive cards

thread-management.md#

useThreadManager hook, loading threads, processing messages, conversation persistence

styling.md#

CSS scoping, theme integration, card animations, dark mode patterns

gotchas.md#

Hard-won lessons: remount flashing, history building, response format parsing, state machine quirks


Skill Status: COMPLETE

Line Count: ~140

Progressive Disclosure: 6 resource files

---
name: crayonchat
description: CrayonChat (@crayonai/react-core, @crayonai/react-ui) integration guide. AI chat UI framework for building conversational interfaces with SSE streaming, custom response templates, thread management, and interactive cards. Covers CrayonChat component props, useThreadManager, useThreadState, useThreadActions, processStreamedMessage, template registration, conversation starters, welcome messages, and custom loading indicators.
---

# CrayonChat Integration Guide

## Purpose

Battle-tested patterns for building AI chat interfaces with `@crayonai/react-core` and `@crayonai/react-ui`.

## When to Use This Skill

Activates when:
- Working with CrayonChat, `@crayonai/react-core`, or `@crayonai/react-ui`
- Building AI chat interfaces with SSE streaming
- Creating custom response templates for chat
- Managing conversation threads with `useThreadManager`
- Implementing `processMessage` callbacks

---

## Quick Start

```tsx
import { useThreadManager, processStreamedMessage } from "@crayonai/react-core";
import { CrayonChat } from "@crayonai/react-ui";
import "@crayonai/react-ui/styles/index.css";

const templates = [
  { name: "my_card", Component: MyCard },
];

function Chat() {
  const threadManager = useThreadManager({
    threadId: null,
    loadThread: async (id) => [],
    onProcessMessage: async ({ message, threadManager: tm, abortController }) => {
      const response = await myProcessMessage({
        threadId: "new",
        messages: tm.messages.concat({ ...message, id: crypto.randomUUID() }),
        abortController,
      });
      await processStreamedMessage({
        response,
        createMessage: tm.appendMessages,
        updateMessage: tm.updateMessage,
        deleteMessage: tm.deleteMessage,
      });
      return [];
    },
    responseTemplates: templates,
  });

  return (
    <CrayonChat
      type="standalone"
      threadManager={threadManager}
      responseTemplates={templates}
      agentName="Assistant"
      logoUrl="/logo.svg"
      scrollVariant="always"
      messageLoadingComponent={() => <MyLoader />}
      welcomeMessage={{ title: "Hello", description: "How can I help?" }}
      conversationStarters={{
        variant: "long",
        options: [{ displayText: "Get started", prompt: "Help me get started" }],
      }}
    />
  );
}
```

---

## Navigation Guide

| Need to...                        | Read this                                |
|-----------------------------------|------------------------------------------|
| Understand CrayonChat props       | [component-api.md](component-api.md)     |
| Build SSE streaming               | [sse-streaming.md](sse-streaming.md)     |
| Create custom templates           | [templates.md](templates.md)             |
| Manage threads & state            | [thread-management.md](thread-management.md) |
| Style & theme the chat            | [styling.md](styling.md)                 |
| Avoid common pitfalls             | [gotchas.md](gotchas.md)                 |

---

## Core Architecture

```
CrayonChat (UI shell)
  ├── threadManager (state + actions)
  │   ├── messages[]  ← UserMessage | AssistantMessage
  │   ├── isRunning   ← true while streaming
  │   └── processMessage() → SSE Response → processStreamedMessage()
  ├── responseTemplates[] ← { name, Component }
  │   └── Templates use useThreadActions() / useThreadState()
  └── SSE stream format:
      event: text\ndata: word\n\n        ← streamed text
      event: tpl\ndata: {name, templateProps}\n\n  ← template card
```

---

## Key Types (from @crayonai/react-core)

```typescript
// Messages
type UserMessage = { id: string; role: "user"; type: "prompt"; message?: string; context?: JSONValue[] };
type AssistantMessage = {
  id: string; role: "assistant"; context?: JSONValue[];
  message?: ({ type: "text"; text: string } | { type: "template"; name: string; templateProps: any })[];
};
type Message = UserMessage | AssistantMessage;
type CreateMessage = Omit<UserMessage, "id">;

// Templates
interface ResponseTemplate { name: string; Component: React.ComponentType<any> }

// Thread state
type ThreadState = {
  isRunning?: boolean;
  isLoadingMessages?: boolean;
  messages: Message[];
  error: Error | null | undefined;
  responseTemplates: Record<string, ResponseTemplate>;
  isInitialized: boolean;
};

// Thread actions
type ThreadActions = {
  processMessage: (message: CreateMessage) => Promise<void>;
  appendMessages: (...messages: Message[]) => void;
  updateMessage: (message: Message) => void;
  deleteMessage: (messageId: string) => void;
  onCancel: () => void;
  setMessages: (messages: Message[]) => void;
};
```

---

## Quick Reference

**Imports:**
```typescript
// Core
import { useThreadManager, useThreadState, useThreadActions, processStreamedMessage } from "@crayonai/react-core";
import type { Message, UserMessage, AssistantMessage, CreateMessage, ResponseTemplate, ThreadManager } from "@crayonai/react-core";

// UI
import { CrayonChat, CheckBoxGroup, CheckBoxItem } from "@crayonai/react-ui";
import "@crayonai/react-ui/styles/index.css";
```

**CrayonChat types:** `"standalone"` | `"copilot"` | `"bottom-tray"`

**Scroll variants:** `"always"` (auto-scroll) is the safe default

**Template component hooks:**
- `useThreadActions()` → `{ processMessage }` — send follow-up messages from cards
- `useThreadState()` → `{ isRunning, messages }` — read current state in templates

---

## Resource Files

### [component-api.md](component-api.md)
Complete CrayonChat component props, welcome message config, conversation starters

### [sse-streaming.md](sse-streaming.md)
SSE stream format, processMessage callback, building Response objects, text chunking

### [templates.md](templates.md)
Template registry, creating custom templates, template props, interactive cards

### [thread-management.md](thread-management.md)
useThreadManager hook, loading threads, processing messages, conversation persistence

### [styling.md](styling.md)
CSS scoping, theme integration, card animations, dark mode patterns

### [gotchas.md](gotchas.md)
Hard-won lessons: remount flashing, history building, response format parsing, state machine quirks

---

**Skill Status**: COMPLETE
**Line Count**: ~140
**Progressive Disclosure**: 6 resource files

Keyboard Shortcuts

Search in document⌘K
Focus search/
Previous file tab
Next file tab
Close overlayEsc
Show shortcuts?