Pika's web component system allows agents to render rich, interactive widgets in multiple contexts throughout the chat interface. This page explains how the web component rendering system works and enables dynamic, AI-driven interfaces.
Multi-Context Rendering
Section titled “Multi-Context Rendering”Traditional chatbots provide static responses with pre-built UI components. Pika's web component system allows widgets to render in four different contexts:
Rendering Contexts
Section titled “Rendering Contexts”Inline Context:
- Embedded within chat messages as part of the conversation flow
- Use for data visualizations, interactive forms, rich media previews
- Example:
<chart type="bar" data='...' title="Revenue" />
Spotlight Context:
- Persistent dashboard carousel above the chat input
- Provides quick access to frequently used tools and information
- User can pin/unpin widgets
- Example: Weather dashboard, recent activity summary, quick actions
Dialog Context:
- Modal overlays for focused interactions
- Full-screen attention on a specific task or form
- Example: Configuration wizards, detailed forms, confirmations
Canvas Context:
- Split-screen workspace for complex, rich interfaces
- Similar to Claude Artifacts
- Example: Document editors, data explorers, report generators
How Component Rendering Works
Section titled “How Component Rendering Works”Step 1: Agent Generates Tag
Section titled “Step 1: Agent Generates Tag”Agent decides to render a component:
<weather-dashboard location="San Francisco" view="detailed" />Step 2: Tag Detection
Section titled “Step 2: Tag Detection”Frontend parses message for XML tags:
// Message parsingconst segments = parseMessageContent(message.content);// Segments include text and tag elementsStep 3: Tag Definition Lookup
Section titled “Step 3: Tag Definition Lookup”System retrieves tag definition from registry:
const tagDefinition = await loadTagDefinition('weather-dashboard');Tag definition specifies:
- Component URL (where to load from)
- Rendering contexts (inline, spotlight, dialog, canvas)
- Usage mode (global vs. chat-app specific)
- Status (enabled, disabled, retired)
Step 4: Component Loading
Section titled “Step 4: Component Loading”System loads web component from URL or S3:
// Dynamic importconst componentModule = await import(tagDefinition.componentUrl);
// Register custom elementcustomElements.define('weather-dashboard', WeatherDashboard);Step 5: Context Injection
Section titled “Step 5: Context Injection”Component receives application and chat state:
import { getPikaContext } from 'pika-shared/util/wc-utils';
// In componentconst context = await getPikaContext($host());
// Context provides:// - appState (global app state)// - chatAppState (current chat app state)// - chatState (current session state)Step 6: Component Rendering
Section titled “Step 6: Component Rendering”Component renders in designated container:
// For inline context<div class="message-tag-container"> <weather-dashboard location="San Francisco" /></div>
// For spotlight context<div class="spotlight-widget"> <weather-dashboard location="San Francisco" /></div>
// For canvas context<div class="canvas-view"> <weather-dashboard location="San Francisco" /></div>Tag Definition System
Section titled “Tag Definition System”Tag Configuration
Section titled “Tag Configuration”Tags are defined in pika-config.ts:
export const tags: TagDefinition[] = [ { id: 'weather-dashboard', name: 'Weather Dashboard', description: 'Interactive weather information widget',
// Component location componentUrl: 's3://my-bucket/components/weather-dashboard.js',
// Which contexts support this tag renderingContexts: { inline: { enabled: true }, spotlight: { enabled: true }, dialog: { enabled: true }, canvas: { enabled: true } },
// Availability model usageMode: 'global', // or 'chat-app' for opt-in
status: 'enabled' }];Usage Modes
Section titled “Usage Modes”Global Tags (usageMode: "global"):
- Automatically available to all chat apps
- Chat apps can disable specific global tags if needed
- Use for widely applicable components
Chat-App Tags (usageMode: "chat-app"):
- Must be explicitly enabled by each chat app
- Use for specialized or domain-specific components
- Opt-in model for better control
Chat App Tag Control
Section titled “Chat App Tag Control”Chat apps control which tags they use:
chatApp: { features: { tags: { tagsEnabled: ['specialized-widget', 'domain-tool'], tagsDisabled: ['global-widget-i-dont-want'] } }}Component Development
Section titled “Component Development”Building a Web Component
Section titled “Building a Web Component”Components are Svelte components with custom element support:
<svelte:options customElement={{ tag: 'my-widget', shadow: 'none' }} />
<script lang="ts"> import type { PikaWCContext } from 'pika-shared/types/chatbot/webcomp-types'; import { getPikaContext } from 'pika-shared/util/wc-utils';
// Props from tag attributes export let location: string = ''; export let view: string = 'summary';
let context = $state<PikaWCContext>(); let data = $state<any>();
$effect(() => { loadContext(); });
async function loadContext() { context = await getPikaContext($host()); await loadData(); }
async function loadData() { // Access agent directly without visible chat const response = await context.chatAppState.invokeChatWithoutSession({ prompt: `Get weather for ${location}`, responseFormat: 'json', responseSchema: WeatherSchema });
data = response; }</script>
<div class="widget-container"> {#if data} <h3>{location} Weather</h3> <div class="temperature">{data.temperature}°F</div> <div class="condition">{data.condition}</div> {:else} <div>Loading...</div> {/if}</div>
<style> .widget-container { padding: 1rem; border-radius: 0.5rem; background: var(--widget-bg); }</style>Component Context API
Section titled “Component Context API”Components have access to powerful context:
context.appState - App-level state and configuration
context.chatAppState - setOrUpdateCustomTitleBarAction() - Add custom actions - renderTag() - Render other tags programmatically - invokeChatWithoutSession() - Direct agent invocation - setSpotlightWidgets() - Manage spotlight widgets
context.chatState - currentSession - Session metadata - messages - Conversation history - subscribe() - React to state changesDirect Agent Invocation
Section titled “Direct Agent Invocation”Components can invoke agents without creating visible sessions:
// Get structured data from agentconst response = await context.chatAppState.invokeChatWithoutSession({ prompt: 'Get the top 5 trending topics', responseFormat: 'json', responseSchema: TrendingTopicsSchema,
// Override agent instructions for this call agentInstructionOverride: 'Return only the data as JSON, no explanation.'});
// Response is typed and structuredconst topics: TrendingTopic[] = response.topics;Use cases:
- Real-time data fetching (weather, stock prices, metrics)
- Background updates for dashboard widgets
- Interactive widgets with live data
- Component-specific LLM interactions
Static Context Components
Section titled “Static Context Components”Components can initialize features without visible UI:
// Tag with static contextrenderingContexts: { static: { enabled: true, shutDownAfterMs: 5000 // Optional cleanup }}Static components can:
- Register custom title bar actions
- Initialize app-level services
- Set up global event listeners
- Perform one-time setup tasks
Example:
<svelte:options customElement={{ tag: 'my-app-init', shadow: 'none' }} />
<script lang="ts"> import { getPikaContext } from 'pika-shared/util/wc-utils';
$effect(() => { initializeApp(); });
async function initializeApp() { const context = await getPikaContext($host());
// Register custom action in title bar context.chatAppState.setOrUpdateCustomTitleBarAction({ id: 'quick-report', type: 'action', title: 'Generate Report', iconSvg: await getIconSvg('file-text'), callback: async () => { await context.chatAppState.renderTag('report-widget', 'canvas'); } }); }</script>
<!-- No visible UI -->Built-in Components
Section titled “Built-in Components”Pika includes several built-in components:
Chart Component
Section titled “Chart Component”<chart type="bar" data='{"labels": ["Q1", "Q2", "Q3", "Q4"], "datasets": [{"data": [100, 150, 200, 175]}]}' title="Quarterly Revenue" />Prompt Component
Section titled “Prompt Component”<prompt>Compare the weather last year also.</prompt>Renders as clickable suggestion that continues the conversation.
Image Component
Section titled “Image Component”<image src="https://example.com/image.jpg" alt="Description" />Download Component
Section titled “Download Component”<download url="https://example.com/report.pdf" filename="report.pdf">Download Report</download>Component Lifecycle
Section titled “Component Lifecycle”Loading States
Section titled “Loading States”let loading = $state(true);let error = $state<string>();
async function init() { try { loading = true; await loadData(); } catch (err) { error = err.message; } finally { loading = false; }}Context Switching
Section titled “Context Switching”Components that support multiple contexts can adapt:
// Detect current rendering contextconst renderContext = context.chatAppState.getCurrentRenderContext();
if (renderContext === 'spotlight') { // Show compact view} else if (renderContext === 'canvas') { // Show full, rich view}Cleanup
Section titled “Cleanup”import { onDestroy } from 'svelte';
let subscription: () => void;
$effect(() => { subscription = context.chatState.subscribe((state) => { // React to state changes });});
onDestroy(() => { if (subscription) { subscription(); }});Security and Access Control
Section titled “Security and Access Control”Component Isolation
Section titled “Component Isolation”- Components run in same security context as chat app
- No direct backend access (must use agent or APIs)
- Agent handles all authentication and authorization
- Tools enforce access control
Agent-Based Data Access
Section titled “Agent-Based Data Access”All data access goes through agents:
// Agent enforces securityconst data = await context.chatAppState.invokeChatWithoutSession({ prompt: 'Get user orders', // User context automatically included});
// Tool checks userId, entityId, permissions// Agent never exposes data user shouldn't seePerformance Considerations
Section titled “Performance Considerations”Lazy Loading
Section titled “Lazy Loading”- Components loaded on-demand
- Only when tag is rendered
- Cached after first load
Code Splitting
Section titled “Code Splitting”- Each component is separate bundle
- No impact on main app bundle size
- Fast initial load
Efficient Updates
Section titled “Efficient Updates”- Components re-render only when props or state change
- Svelte's fine-grained reactivity
- Minimal DOM updates
Related Documentation
Section titled “Related Documentation”- Request Lifecycle - How messages flow through system
- Agent Execution Flow - Agent processing
- Project Structure - Where components live
- Frontend Architecture - Chat app design