Custom UI components are currently only supported in the React version of the widget. If you’re using another framework, you’ll need to use the React widget to enable this functionality.

Instead of plain text responses, your AI can render custom React components to display:

  • Interactive forms
  • Data visualizations
  • Product cards
  • Settings controls
  • Any custom UI you build

Quick Example

Let’s create a simple weather card component that displays temperature and wind data.

1

Create an Action

Create a new action called getWeather that fetches weather data. You can do this either:

For this example, we’ll create an action with these parameters:

GET https://api.open-meteo.com/v1/forecast
?latitude=52.52
&longitude=13.41
&current=temperature_2m
&hourly=temperature_2m,wind_speed_10m

The action name (getWeather) is what you’ll use to link your component to this action later.

2

Create Your Component

Make a new file WeatherCard.tsx:

import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { ComponentProps } from "react";

type WeatherData = {
  current: {
    temperature_2m: number;
    wind_speed_10m: number;
  };
}

export function WeatherCard({ data }: ComponentProps<WeatherData>) {
  return (
    <Card className="w-full max-w-sm">
      <CardHeader>
        <CardTitle>Current Weather</CardTitle>
      </CardHeader>
      <CardContent>
        <div className="flex justify-between">
          <div>
            <p className="text-2xl font-bold">
              {data.current.temperature_2m}°C
            </p>
            <p className="text-gray-500">Temperature</p>
          </div>
          <div>
            <p className="text-2xl font-bold">
              {data.current.wind_speed_10m} km/h
            </p>
            <p className="text-gray-500">Wind Speed</p>
          </div>
        </div>
      </CardContent>
    </Card>
  );
}
3

Register the Component

Add the component to your widget setup:

import { Widget, WidgetRoot } from "@opencx/widget/basic";
import { WeatherCard } from "./WeatherCard";

function App() {
  return (
    <WidgetRoot
      options={{
        token: "YOUR_TOKEN",
        components: [
          {
            key: "getWeather", // Matches your action name
            component: WeatherCard,
          },
        ],
      }}
    >
      <Widget />
    </WidgetRoot>
  );
}

How It Works

  1. When your AI needs to show weather data, it calls the getWeather action
  2. The action returns weather data from the API
  3. The widget finds the matching component (WeatherCard)
  4. Your component renders with the action’s data

That’s it! Now when users ask about weather, they’ll see your custom card component instead of plain text.

Component Props

Your components receive these props:

interface ComponentProps<T = any> {
  data: T; // Data from your action
  isLoading?: boolean; // Loading state
  error?: string; // Error message if action failed
}

Best Practices

  • Use descriptive component and action names
  • Handle loading and error states
  • Use shadcn/ui components for consistent styling
  • Keep components focused and reusable
  • Type your action response data

Examples

You can build UI components for:

  • Product catalogs
  • Booking forms
  • Data dashboards
  • Settings panels
  • File uploads
  • And much more!

Need more examples? Check our GitHub repo.

Remember to: - Match component key with action name - Handle the expected data structure - Test with loading/error states