Quick Start

Getting Started with ASH UI

Build beautiful agent chat interfaces in minutes. ASH UI is a React component library designed specifically for Claude Agent SDK applications.

What You'll Build

By the end of this guide, you'll have a working chat interface with streaming messages, tool call visualization, and theme support.

Chat Interface

MessageList with user and assistant messages

Tool Visualization

See tool calls and results in real-time

Dark/Light Themes

Built-in theming with persistence

1

Install the Package

Install ASH UI and its peer dependencies using your preferred package manager:

npm
npm install @ash-cloud/ash-ui
pnpm
pnpm add @ash-cloud/ash-ui
yarn
yarn add @ash-cloud/ash-ui

Peer Dependencies

ASH UI requires react ^18.0.0 and react-dom ^18.0.0. Most projects already have these installed.

2

Import Styles

Add the stylesheet to your app's entry point. Choose based on your setup:

Recommended

With Tailwind CSS

If your project already uses Tailwind

app.tsx
import '@ash-cloud/ash-ui/styles.css';

Without Tailwind CSS

Standalone apps without Tailwind

app.tsx
import '@ash-cloud/ash-ui/styles-full.css';
3

Add ThemeProvider

Wrap your application with the ThemeProvider to enable dark/light mode:

app.tsx
import { ThemeProvider } from '@ash-cloud/ash-ui';
import '@ash-cloud/ash-ui/styles.css';

function App() {
  return (
    <ThemeProvider defaultTheme="dark" storageKey="my-app-theme">
      {/* Your app content */}
    </ThemeProvider>
  );
}

export default App;

Tip: The ThemeProvider automatically persists the user's theme preference to localStorage using the storageKey you provide.

4

Create Your First Chat Interface

Now let's build a complete chat interface with the useAgentChat hook and MessageList component:

ChatInterface.tsx
import { useState } from 'react';
import {
  MessageList,
  ThemeProvider,
  useAgentChat,
} from '@ash-cloud/ash-ui';
import '@ash-cloud/ash-ui/styles.css';

function ChatInterface() {
  const [input, setInput] = useState('');

  // Connect to your agent API
  const {
    entries,           // Normalized message entries
    isStreaming,       // Whether agent is responding
    streamingContent,  // Current streaming text
    sendMessage,       // Send a message
    stopExecution,     // Stop current response
  } = useAgentChat({
    apiBasePath: '/api/agent',  // Your agent API endpoint
    sessionId: 'my-session',
  });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (input.trim() && !isStreaming) {
      sendMessage(input);
      setInput('');
    }
  };

  return (
    <div className="flex flex-col h-screen bg-surface-dark">
      {/* Messages */}
      <div className="flex-1 overflow-hidden">
        <MessageList
          entries={entries}
          loading={isStreaming && entries.length === 0}
          streamingContent={isStreaming ? streamingContent : undefined}
        />
      </div>

      {/* Input */}
      <form onSubmit={handleSubmit} className="p-4 border-t border-white/10">
        <div className="flex gap-3">
          <input
            type="text"
            value={input}
            onChange={(e) => setInput(e.target.value)}
            placeholder="Type a message..."
            className="flex-1 px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder:text-white/40 focus:outline-none focus:border-accent/50"
            disabled={isStreaming}
          />
          <button
            type="submit"
            disabled={isStreaming || !input.trim()}
            className="px-6 py-3 bg-accent text-black font-medium rounded-xl hover:bg-accent-500 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
          >
            {isStreaming ? 'Sending...' : 'Send'}
          </button>
        </div>
      </form>
    </div>
  );
}

export default function App() {
  return (
    <ThemeProvider defaultTheme="dark">
      <ChatInterface />
    </ThemeProvider>
  );
}
5

Understanding the Data Flow

The useAgentChat hook handles all the complexity of agent communication:

How it works:

  1. 1.User sends a message via sendMessage()
  2. 2.Hook creates SSE connection to your agent API
  3. 3.Streaming events are parsed and normalized into entries
  4. 4.MessageList renders messages, tool calls, and results automatically

Entry Types

The entries array contains normalized message entries:

types.ts
type NormalizedEntry = {
  id: string;
  type: 'user' | 'assistant' | 'tool_call' | 'tool_result';
  content?: string;
  toolCall?: {
    name: string;
    args: Record<string, unknown>;
    status: 'pending' | 'running' | 'completed' | 'error';
    result?: unknown;
  };
  timestamp: Date;
};

What's Next?

Need the Full Stack?

ASH UI is part of the ASH ecosystem. Use it with the Agent SDK Harness for complete session management, persistence, and streaming infrastructure.