Guidelines for contributing new components to Tool UI.
This page is for people working inside this repo. It covers building or modifying components, not app integration.
Component Structure
Each component lives in components/component-name/ with this file organization:
Core Files
schema.ts: Serializable schema and parser (serializableXSchema, parseSerializableX)
types.ts: TypeScript type definitions (serializable props, client props, internal types)
component-name.tsx: Main component implementation
index.tsx: Public exports (component, types, parser, error boundary)
error-boundary.tsx: Error boundary wrapper for the component
Sub-Components (as needed)
header.tsx, body.tsx, footer.tsx, actions.tsx: Smaller composable pieces
context.tsx: React context for shared state within the component
Utilities
_ui.tsx: Re-exports shadcn/ui primitives (adapter pattern so consumers can swap them)
_cn.ts: Utility functions (typically just re-exports cn)
formatters.tsx: Data formatting helpers (if needed)
utilities.ts: Component-specific helper functions
Documentation
README.md: Component overview, features, installation, API reference
usage-docs.md: Detailed usage examples and patterns (optional)
Implementation Checklist
Define the Contract
Before coding:
- Intent: One-sentence purpose in a chat context (for example, "Inline approval prompt for destructive actions.")
- Role: One of: information, decision, control, observability, or composite (see UI Guidelines)
- Serializable schema: Design the Zod schema with JSON-serializable props only (no functions, Dates, class instances)
- Required vs optional fields: Which props are essential? Which have safe defaults?
- States: How does it handle loading, empty, error, interactive, and receipt states?
- Addressability: How are rows, actions, and sub-elements identified so the assistant can refer to them in later turns?
- Actions: What actions can users take, and what parameters do we send back to the app?
Build the Component
Implementation pattern:
schema.ts: Define serializableXSchema and parseSerializableX(input: unknown)
types.ts: Create:
- Serializable props type (derived from the schema)
- Client props type (serializable props + callbacks like
onAction, onBeforeAction)
_ui.tsx: Re-export the shadcn/ui primitives you need (buttons, cards, and so on)
component-name.tsx: Build the main component, focused on:
- Single intent and role
- Lifecycle states from UI Guidelines
- Minimal but clear chrome
- Sub-components: Extract header/body/footer/actions when the component grows
context.tsx: Use React context only when sub-components genuinely need shared state
error-boundary.tsx: Wrap the component so bad props don't crash the whole chat
index.tsx: Export the public API (component, types, parser, error boundary)
Document Usage
Create clear documentation:
- README.md: Features, installation instructions, basic usage, props table
- usage-docs.md (optional): Advanced patterns, edge cases, integration examples
- Include at least one example that shows:
- Tool definition with
outputSchema
- Server-side execution returning serializable props
- Client-side
parseSerializableX + <Component /> usage
- Document action handling and any receipt behavior
Verify Quality
Ensure the component meets standards:
- Accessibility: WCAG AA contrast, usable touch targets, keyboard navigation, screen reader labels
- Responsive: Works on mobile (320px) through desktop widths
- No scroll traps: Avoid inner scroll areas when possible; let the chat container handle scrolling
- Reduced motion: Respect
prefers-reduced-motion for animations
- Error handling: Validate props, show graceful error states, use error boundaries
- Performance: Avoid unnecessary re-renders and layout thrash
Design Principles for Contributors
All components must follow the UI Guidelines. In practice, that means:
- Single intent: One primary job per component instance
- Clear role: Information, decision, control, or observability (composites must name a primary role)
- Conversation-first: Compact, glanceable, and readable in a few seconds
- Receipt-aware: Any action with side effects must have a receipt pattern
- Schema-driven: Props must be derivable from a serializable schema
- Serializable vs client-only: Keep serializable props separate from client-only props (callbacks, ReactNodes, and so on), with clear naming between the two
- Accessible by default: Keyboard, screen reader, and contrast-friendly out of the box
Use UI Guidelines for philosophy; this page is about how to express that philosophy in code.
Submitting Components
When you are ready to contribute:
- Test thoroughly: Check across viewports, themes, and basic assistive tech (screen reader, keyboard only)
- Document completely: README with features, API, and at least one end-to-end example
- Follow conventions: Match existing component patterns and file structure
- Open a PR: Include screenshots or screen recordings, and link to the relevant docs section
Questions? Open an issue on GitHub or reach out on Discord.