> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cycls.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Customize UI

> Learn how to customize your agent's web interface and use native UI components

## Overview

Every agent comes with a beautiful web UI out of the box. The interface supports both static customization (header/intro) and **native UI components** that stream rich content progressively to users.

## Default Web UI

When you deploy your agent, it automatically gets a web interface accessible at your agent's URL. The default UI includes:

* **Chat Interface**: A modern, responsive chat interface
* **Message History**: Automatic conversation history management
* **Real-time Streaming**: Support for streaming responses
* **Native UI Components**: Rich components like thinking bubbles, code blocks, tables, and more
* **Mobile Responsive**: Works seamlessly on all devices
* **Auto RTL Support**: Automatically supports right-to-left languages

## Native UI Components

Cycls provides native UI components that render beautifully in the chat interface. Simply yield structured objects to display rich content that streams progressively.

### Text

Plain strings are rendered as markdown text with full formatting support.

```python theme={null}
@cycls.app()
async def app(context):
    yield "Hello! Here's some **bold** and *italic* text."
    yield "\n\n## A Heading\n\nAnd a paragraph below it."
```

### Thinking Bubbles

Show your agent's reasoning process with collapsible thinking bubbles.

```python theme={null}
@cycls.app()
async def app(context):
    yield {"type": "thinking", "thinking": "Let me analyze this question..."}
    yield {"type": "thinking", "thinking": "Considering multiple approaches..."}
    yield "Based on my analysis, here's the answer:"
```

### Code Blocks

Display syntax-highlighted code with language detection.

```python theme={null}
@cycls.app()
async def app(context):
    yield {
        "type": "code",
        "code": "def hello():\n    print('Hello, World!')",
        "language": "python"
    }
```

### Tables

Stream tables row-by-row for progressive rendering.

```python theme={null}
@cycls.app()
async def app(context):
    yield {
        "type": "table",
        "headers": ["Name", "Age", "City"],
        "rows": [
            ["Alice", "30", "New York"],
            ["Bob", "25", "San Francisco"],
            ["Charlie", "35", "Chicago"]
        ]
    }
```

### Callouts

Display styled callout boxes for important information.

```python theme={null}
@cycls.app()
async def app(context):
    yield {
        "type": "callout",
        "callout": "This operation completed successfully!",
        "style": "success",
        "title": "Success"
    }

    yield {
        "type": "callout",
        "callout": "Please review before proceeding.",
        "style": "warning",
        "title": "Warning"
    }

    yield {
        "type": "callout",
        "callout": "Here's some helpful information.",
        "style": "info",
        "title": "Info"
    }

    yield {
        "type": "callout",
        "callout": "An error occurred during processing.",
        "style": "error",
        "title": "Error"
    }
```

**Available styles:** `success`, `warning`, `info`, `error`

### Images

Display images with optional captions.

```python theme={null}
@cycls.app()
async def app(context):
    yield {
        "type": "image",
        "src": "https://example.com/image.png",
        "alt": "Description of image",
        "caption": "Figure 1: An example image"
    }
```

### Status Indicators

Show progress or status updates during long operations.

```python theme={null}
@cycls.app()
async def app(context):
    yield {"type": "status", "status": "Processing your request..."}
    # ... do work ...
    yield {"type": "status", "status": "Almost done..."}
    yield "Here are the results!"
```

## Component Reference

| Type       | Key Fields                  | Description                   |
| ---------- | --------------------------- | ----------------------------- |
| `text`     | `text`                      | Markdown-formatted text       |
| `thinking` | `thinking`                  | Collapsible reasoning bubble  |
| `code`     | `code`, `language`          | Syntax-highlighted code block |
| `table`    | `headers`, `rows`           | Streaming table               |
| `callout`  | `callout`, `style`, `title` | Styled alert box              |
| `image`    | `src`, `alt`, `caption`     | Image with caption            |
| `status`   | `status`                    | Progress indicator            |

## Header and Intro Customization

You can also customize the header and introduction sections using HTML and TailwindCSS:

```python theme={null}
import cycls
import urllib.parse

header = """
<raw>
<div class="flex flex-col border border-gray-200 rounded-lg bg-white p-6 my-12 max-w-7xl mx-auto">
  <div class="text-3xl font-bold text-gray-900 mb-3">
    Welcome to My Agent
  </div>
  <p class="text-gray-600 text-base">
    Your AI-powered assistant for exploring new ideas.
  </p>
</div>
</raw>
"""

intro = f"""
<div class="py-3">
  <div class="flex flex-wrap gap-3 justify-center">
    <a href="https://cycls.com/send/{urllib.parse.quote('Tell me about Python')}" class="px-4 py-2 border rounded-xl hover:shadow-lg">
      Tell me about Python
    </a>
    <a href="https://cycls.com/send/{urllib.parse.quote('Write a poem')}" class="px-4 py-2 border rounded-xl hover:shadow-lg">
      Write a poem
    </a>
  </div>
</div>
"""

@cycls.app(copy_public=["logo.png"], header=header, intro=intro)
async def app(context):
    yield "Hello! How can I help you today?"

app.local()
```

### Styling Guidelines

* **HTML Support**: Full HTML5 support with inline styles
* **TailwindCSS**: Complete TailwindCSS framework available
* **Raw Tags**: Wrap custom HTML in `<raw>` tags to prevent escaping
* **Responsive Design**: Use TailwindCSS responsive classes for mobile compatibility
* **Clickable Links**: Use `urllib.parse.quote` to encode messages in links

## Clickable Links

Create clickable links that automatically send encoded text to the chat:

```python theme={null}
import urllib.parse

@cycls.app()
async def app(context):
    link = f"https://cycls.com/send/{urllib.parse.quote('Tell me more')}"
    yield f"Click here to [learn more]({link})"
```

### Link Format

```
[Link Text](https://cycls.com/send/Encoded%20message)
```

## Custom Frontend Applications

You can connect any custom frontend to your agent using the OpenAI-compatible API at `/chat/completions` or the Cycls Protocol at `/chat/cycls`.

## Next Steps

<Card title="Agent API" horizontal href="/core-concepts/agent-api">
  Learn about the built-in REST API and streaming protocols.
</Card>
