logoSemaphor

Custom Components

Learn how to render custom components in Semaphor dashboard

Overview

When embedding analytics into your app, you'll often need to display information in industry-specific ways. For example:

  • Trade analytics: A specialized map to visualize imports and exports between countries.
  • Financial apps: A table with precise sorting and filtering options.
  • HR tools: A distribution chart to show candidate demographics.

The sheer variety of visualizations means no single charting library can cover every need. Most BI tools lock you into one or two libraries, offering only limited customization.

This creates a tough choice:

  1. Settle for generic charts, leading to a subpar user experience.
  2. Build everything in-house, investing time and resources in analytics infrastructure instead of your core product.

As your analytics needs evolve, rigid BI tools make it increasingly difficult to adapt. That’s why flexibility is crucial when choosing an embedded analytics solution.

At Semaphor, we’ve designed a pluggable architecture that lets you bring your own charting library—or even fully custom components. This gives you complete control over the user experience, enabling you to integrate interactive, custom visuals with just a few lines of code.

Let's take a look at how it works.

Component Props Interface

Before diving into creating custom components, it's important to understand the props that Semaphor passes to your custom visuals. Every custom component receives a standardized set of props through the SingleInputVisualProps interface:

Component Props Types
export type Data = Record<string, string | number | boolean>[];
 
export type CustomCardTheme = {
  colors?: string[];
  mode?: 'light' | 'dark' | 'system';
};
 
export type SingleInputVisualProps = {
  data: Data;
  settings?: Record<string, string | number | boolean>;
  theme?: CustomCardTheme;
};

Data Prop

The data prop contains the actual data returned by your query. It's an array of records where each record is a key-value object:

  • Type: Record<string, string | number | boolean>[]
  • Description: An array of objects representing rows of data from your query
  • Example:
    const sampleData = [
      { name: 'John Doe', email: 'john@example.com', sales: 2500 },
      { name: 'Jane Smith', email: 'jane@example.com', sales: 3200 },
    ];

This structure allows your component to work with any query result, regardless of the column names or data types.

Settings Prop

The settings prop is a key-value object that allows you to control the behavior and appearance of your component:

  • Type: Record<string, string | number | boolean>
  • Description: Configuration options defined in your components.config.ts file
  • Example:
    const settings = {
      label: 'Sales Dashboard',
      showTotal: true,
      maxItems: 10,
    };

These settings are configurable by users in the Semaphor dashboard and provide a way to customize your component without changing the code.

Theme Prop

The theme prop provides styling information to ensure your component matches the dashboard's appearance:

  • Type: CustomCardTheme
  • Properties:
    • colors: An array of color values for consistent visual styling
    • mode: The current theme mode ('light', 'dark', or 'system')

Example:

const theme = {
  colors: ['#3b82f6', '#ef4444', '#10b981', '#f59e0b'],
  mode: 'dark',
};

Use these colors for charts, backgrounds, and other visual elements to maintain consistency with the overall dashboard theme.

Step 1: Create your custom component

The repository below includes a basic example of how to create a custom component for Semaphor.

Clone the repository
git clone https://github.com/rohitspujari/semaphor-plugin-quickstart.git
cd semaphor-plugin-quickstart
npm install
npm run dev

This starts a local server at http://localhost:5173. You can open up this repository in your favorite code editor and start exploring the code.

In semaphor-components folder, you'll find the custom component that we'll add to the Semaphor dashboard. It's a simple component showing recent sales — about 50 lines of code. It demonstrates how to use all three props (data, settings, and theme) that Semaphor provides to your custom components. You can make your components as sophisticated as you want.

src/components/semaphor-components/recent-sales-card.tsx
import { SingleInputVisualProps } from '../types';
 
export function RecentSales({ data, settings, theme }: SingleInputVisualProps) {
  if (!data || data?.length === 0) return null;
 
  // get the column keys of the data
  const keys = Object.keys(data[0]);
 
  // Use settings to control component behavior
  const label = settings?.label || 'Recent Sales';
  const maxItems = Number(settings?.maxItems) || data.length;
 
  // Use theme colors for styling
  const primaryColor = theme?.colors?.[0] || '#3b82f6';
  const isDarkMode = theme?.mode === 'dark';
 
  // calculate the total sales
  const totalSales = data.reduce(
    (acc, record) => acc + Number(record?.[keys[2]]),
    0
  );
  // format total sales to 2 decimal places and add $ with commas
  const formattedTotalSales = totalSales
    .toFixed(2)
    .replace(/\B(?=(\d{3})+(?!\d))/g, ',');
 
  return (
    <div className="px-4 bg-background rounded-lg">
      <h2 className="text-lg font-semibold">{label}</h2>
      <p className="text-sm text-muted-foreground mb-4">
        You made ${formattedTotalSales} sales this month.
      </p>
      <ul className="p-0">
        {data.slice(0, maxItems).map((record, index) => (
          <li
            key={index}
            className=" flex items-center justify-between py-2 border-b border-muted last:border-none"
          >
            <div className="flex items-center">
              <div className="w-10 h-10 bg-muted rounded-full flex items-center justify-center text-black font-bold">
                {record[keys[0]]
                  ?.toString()
                  .split(' ')
                  .map((n) => n[0])
                  .join('')}
              </div>
              <div className="ml-3">
                <p className="font-medium text-foreground">
                  {record?.[keys[0]]}
                </p>
                <p className="text-sm text-muted-foreground">
                  {record?.[keys[1]]}
                </p>
              </div>
            </div>
            <p className="font-semibold text-foreground">{record?.[keys[2]]}</p>
          </li>
        ))}
      </ul>
    </div>
  );
}

Once you are satisfied with your custom component, you need to export it as a named export in the index.ts file. You can export as many components as you want.

src/components/index.ts
import { RecentSales } from './semaphor-components/recent-sales-card';
 
import '../index.css';
 
export { RecentSales };

And lastly, you will need to specify how this component should appear in Semaphor console with components.config.ts file.

src/components/components.config.ts
export const config: ComponentsConfig = {
  visuals: [
    {
      name: 'Recent Sales', // The name of the component that will appear in the Semaphor dashboard
      icon: 'Table2', // The icon of the component
      component: 'RecentSales', // The component exported in the index.ts file
      settings: {
        label: {
          title: 'Label', // The label property of the component
          defaultValue: 'Recent Sales', // The default value of the label
          ui: 'input', // The UI type of the label property
        },
        maxItems: {
          title: 'Max Items', // Control how many items to display
          defaultValue: 5, // Show 5 items by default
          ui: 'input', // Number input for max items
        },
      },
    },
  ],
};

Step 2: Publish your component to Semaphor

To publish your component to Semaphor, you will first need to install semaphor-cli. You can do it with the following command:

Install semaphor-cli
npm install -g semaphor-cli

Now let's initialize a new Semaphor Plugin for your custom component.

You will be prompted to enter your Semaphor project ID and secret. You can get them from your Semaphor project page.

NOTE: Make sure to initialize the Plugin and NOT the App.

Initialize the plugin
~/plugins/semaphor-plugin-quickstart semaphor init
? What do you want to publish? Plugin
? Enter your plugin name: my_first_plugin
? Enter your build path: dist
? Enter your Semaphor Project ID: p_315108f1-d76e-43ab-af8e-d88ccaae59fe
? Enter your Semaphor Project Secret: [hidden]
Config file created successfully!

Once the cli is initalized, you can build and publish your plugin to Semaphor with the following commands.

Build and publish your plugin
npm run build
semaphor publish

Step 3: Use your custom component in Semaphor Dashboard

You can now use your custom component in Semaphor dashboard just like any other visual from the visual selector and make it interactive with the rest of the dashboard.

Here is the sample dashboard with the custom component added. If you have any questions feel free to get in touch with us at support@semaphor.cloud

On this page