# Usage
Source: https://ui.spencerwueste.com/docs/usage/
Markdown: https://ui.spencerwueste.com/llm/usage.md

> Compose components with slots and context instead of props-for-everything.

Every component is a small family of parts that share context. You compose the parts you need and skip the rest.

## Compose, don't configure

A prompt input is a container, a textarea, and an actions row. Icons and buttons are yours:

```tsx
import { ArrowUpIcon, PaperClipIcon } from "@heroicons-animated/react";
import { Button } from "@/components/ui/button";
import {
  PromptInput,
  PromptInputAction,
  PromptInputActionGroup,
  PromptInputActions,
  PromptInputTextarea,
} from "@/components/pandacoderz-ui/prompt-input";

export function ChatInput({ onSend }: { onSend: (text: string) => void }) {
  return (
    <PromptInput onSubmit={onSend}>
      <PromptInputTextarea />
      <PromptInputActions>
        <PromptInputActionGroup>
          <PromptInputAction asChild tooltip="Attach">
            <Button variant="ghost" size="icon-sm" className="rounded-full">
              <PaperClipIcon size={16} className="flex" />
            </Button>
          </PromptInputAction>
        </PromptInputActionGroup>
        <PromptInputActionGroup>
          <PromptInputAction asChild tooltip={{ content: "Send", shortcut: "↵" }}>
            <Button size="icon-sm" className="rounded-full">
              <ArrowUpIcon size={16} className="flex" />
            </Button>
          </PromptInputAction>
        </PromptInputActionGroup>
      </PromptInputActions>
    </PromptInput>
  );
}
```

## `asChild`

Parts that wrap interactive elements accept `asChild`. The part then merges its behavior (tooltip, data attributes, click handling) into your element instead of rendering an extra wrapper.

## Icons

The library uses [Heroicons Animated](https://www.heroicons-animated.com). Each icon renders a `div` wrapper around an SVG and takes a numeric `size`, so pass `size={16}` and `className="flex"` for inline alignment. Swap in any icon set you like; nothing depends on the specific package beyond the defaults inside `thread`, `reasoning`, `tool`, and `model-selector`.

## Streaming

Components that render text (`MessageMarkdown`, `ReasoningContent`) accept partial markdown and re-render as it grows. Wrap the conversation in `Thread` so the viewport follows new content, and drive state with a functional update:

```ts
for await (const chunk of stream) {
  setText((prev) => prev + chunk);
}
```

See the [AI Chat block](/docs/blocks/ai-chat) for a complete reducer over reasoning, tool, and text events.

## Styling

Override any part with `className`. Every part sets a `data-slot` attribute, so you can also target them from a parent:

```css
[data-slot="message-content"] {
  max-width: 65ch;
}
```
