Custom Components
Build custom visualizations and filters for Semaphor dashboards using React, TypeScript, and Tailwind CSS.
Table of Contents
- Introduction
- Quick Start
- AI-Assisted Development with Claude
- Project Structure
- Local Development
- Building Custom Visuals
- Building Custom Filters
- Props Reference
- Rendering Inline Filters
- Settings & Configuration
- Theming
- Documentation & Metadata
- Publishing to Semaphor
- Troubleshooting
- Best Practices
- API Reference
- Support
Introduction
Why Custom Plugins?
When embedding analytics into your application, standard chart libraries often fall short. You need:
- Trade Analytics: Specialized maps showing import/export flows with custom annotations
- Financial Dashboards: Tables with precise formatting, sorting, and conditional highlighting
- HR Tools: Demographic visualizations with specific compliance requirements
- Healthcare: Patient flow diagrams, appointment timelines, resource utilization views
Most BI tools lock you into their charting libraries. Semaphor's plugin architecture gives you complete control to bring your own React components, style them your way, and integrate seamlessly with Semaphor platform.
Micro Frontend Architecture
Semaphor plugins follow the micro frontend pattern—small, independently deployable frontend applications that integrate into a larger platform at runtime.
One Plugin, Many Components — Bundle all your custom visuals and filters into a single plugin. This keeps the bundle size small by sharing dependencies (React, Tailwind, utilities) across components.
Why Micro Frontends?
| Benefit | Description |
|---|---|
| Independent Deployment | Ship plugin updates without touching Semaphor core |
| Technology Freedom | Use any React-compatible libraries (D3, Recharts, ECharts, etc.) |
| Team Autonomy | Different teams can own different visualizations |
| Isolation | Plugin bugs don't crash the main application |
| Lightweight | Only load what you need, when you need it |
What You Can Build
| Component Type | Description | Examples |
|---|---|---|
| Custom Visuals | React components that render query data | Charts, tables, KPIs, maps, Gantt charts, custom widgets |
| Custom Filters | Alternative filter UI components | Chip selectors, sliders, calendar pickers, tree selectors |
How It Works
Semaphor handles the hard parts:
- Security and Multi-tenancy
- State management and cascading
- Authentication and permissions
- Query processing and data fetching
- Theme synchronization
- Caching and performance optimization
You focus on:
- Your visualization
- Your styling preferences
- Your user experience
Quick Start
Prerequisites
- Node.js v18 or higher
- npm or yarn
- Semaphor account with an active project
Installation
Development
This opens the Showcase Gallery at http://localhost:5173 where you can:
- Preview all custom visuals and filters
- Test with sample data
- View component documentation
Build & Publish
Your plugin is now available in Semaphor's chart type selector.
AI-Assisted Development with Claude
Speed up your plugin development using Claude Code. The quickstart project includes specialized skills that let Claude generate complete, working components in a single shot.
Quick Generation with Claude
Claude will ask for a component name and description, then automatically:
- Create the component file with proper TypeScript types
- Create sample data for the Showcase
- Register the component in
components.config.ts - Export from
index.ts - Add to
sample-data-registry.ts
Example Workflow
Available Skills
| Skill | Command | Description |
|---|---|---|
| Create Visual | /create-visual | Generate a custom visualization component |
| Create Filter | /create-filter | Generate a custom filter component |
Tips for Better Results
- Be specific: "A donut chart with center label showing percentage" works better than "a chart"
- Mention libraries: "Use Recharts for the bar chart" if you have a preference
- Describe interactions: "Clicking a bar should highlight it and show a tooltip"
After Claude generates your component, you can ask follow-up questions to refine it:
Project Structure
Key Files Reference
| File | Purpose | When to Edit |
|---|---|---|
semaphor-components/*/[name].tsx | React component implementation | Creating/editing components |
semaphor-components/*/[name].data.ts | Sample data for Showcase | Creating components |
components.config.ts | Plugin manifest (names, settings, docs) | Adding/configuring components |
index.ts | Component exports | Adding components |
sample-data-registry.ts | Links components to sample data | Adding components |
Local Development
The Showcase Gallery
When you run npm run dev, the Showcase Gallery provides a preview of your components. The showcase gives you:
- Instant Feedback — Hot reload shows changes immediately
- Realistic Testing — Sample data mimics production query results
- Documentation Preview — See how docs will appear in Semaphor
- Filter Interaction — Test filter components with real state management
Sample Data Convention
Each component has a corresponding .data.ts file with sample data:
Building Custom Visuals
Step 1: Create Component Files
Step 2: Implement the Component
Step 3: Add Sample Data
Step 4: Register in Configuration
Step 5: Export the Component
Step 6: Register Sample Data
Step 7: Test & Publish
Building Custom Filters
Custom filters provide alternative UIs for filtering data. Semaphor handles all filter logic—your component only renders the interface.
Filter Component Example
Filter Configuration
Filter Sample Data
Props Reference
SingleInputVisualProps
Props received by custom visual components.
CustomFilterProps
Props received by custom filter components.
Rendering Inline Filters in Your Custom Components
When building custom visuals, you may want to include filter controls directly inside your component. Semaphor supports this through inline filters—filter controls that appear inside the visual card itself, allowing users to filter data for just that specific card.
Even better, these inline filters can use your own custom filter components (like the ChipFilter example earlier), giving you a fully customized experience—custom visuals with custom filters, all working together.
Why inline filters?
- Self-contained visuals — Users can interact with filters without leaving the card
- Card-specific filtering — Unlike global dashboard filters, inline filters only affect the card they're in
- Flexible placement — You decide where filters appear in your layout (header, sidebar, etc.)
Semaphor passes pre-rendered filter components to your visual via the inlineFilters prop. Your job is simply to render them wherever you want.
How It Works
Semaphor manages:
- Fetching filter options from the database
- Storing selected values
- Re-running queries when filters change
- Persisting filter state
You just render them:
Placement Options
You can render inline filters anywhere in your component:
Best Practices
- Always default to
[]:inlineFilters = [] - Check before rendering:
inlineFilters.length > 0 - Provide visual container: Add background/border for clarity
- Consider mobile: Use
flex-wrapfor responsiveness
Settings & Configuration
Settings allow users to customize your component without code changes.
Defining Settings
Reading Settings
Important: Setting values are always strings from the configuration UI. Parse them as needed.
Theming
Use the theme prop to match the dashboard's visual style.
Using Theme Colors
Tailwind Dark Mode
If you're using Tailwind, leverage dark mode classes:
Documentation & Metadata
Documentation you define in components.config.ts appears in the Semaphor UI.
Documentation Structure
Where Documentation Appears
Publishing to Semaphor
One-Time Setup
During initialization, provide:
- Type:
Plugin - Name: Your plugin name (e.g.,
my-company-charts) - Build path:
dist - Project ID: From Semaphor project settings
- Project Secret: From Semaphor project settings
Build & Publish Workflow
This uploads:
dist/index.js— Your bundled componentsdist/style.css— Your stylesdist/manifest.json— Component metadata
Using in Semaphor
- Open your dashboard in Semaphor
- Add or edit a card
- Click the chart type selector
- Find your visual under "Custom Visuals"
- Configure data source and settings
- Optionally configure inline filters
Troubleshooting
"Element type is invalid. Received a promise that resolves to: undefined"
This is the most common error. It occurs when component names don't match across files.
Symptom:
Cause: Name mismatch between configuration and exports.
Example of the problem:
Fix: Ensure all three names match exactly:
Checklist when renaming components:
- Function name in
.tsxfile - Export in
index.ts -
componentfield incomponents.config.ts - Key in
sample-data-registry.ts - Run
npm run build
Component Not Appearing in Semaphor
- Verify export: Is the component exported in
index.ts? - Check config: Is it registered in
components.config.ts? - Verify names match: Does
componentexactly match the exported function name? - Rebuild and republish:
npm run build && semaphor publish - Hard refresh: Clear browser cache and reload Semaphor
Data is Empty
- Configure data source: Ensure the card has a data source in Semaphor
- Check column names: Verify you're accessing correct column names
- Add debugging:
console.log('data:', data)to inspect - Handle empty state: Always check
if (!data || data.length === 0)
Inline Filters Not Showing
- Default to
[]: EnsureinlineFilters = []in props - Check conditional: Use
inlineFilters.length > 0 - Configure in Semaphor: Inline filters must be added to the card in Semaphor's UI
Styling Issues
- Import CSS: Verify
../index.cssis imported inindex.ts - Tailwind classes: All Tailwind utilities are available
- Theme colors: Use
theme?.colorsfor consistent colors - Dark mode: Check
theme?.mode === 'dark'
Showcase Not Showing Component
- Check registry: Is the component registered in
sample-data-registry.ts? - Verify key: The registry key must match the component name exactly
- Check data file: Ensure
.data.tsexportssampleData
Build Errors
Best Practices
Component Design
- Handle empty states — Always check for missing/empty data
- Provide defaults — Default all settings and props
- Use theme colors — Match the dashboard aesthetic
- Support dark mode — Check
theme?.mode - Be responsive — Use flex/grid layouts
Performance
- Memoize calculations — Use
useMemofor expensive computations - Virtualize long lists — Consider virtual scrolling for large datasets
- Lazy load — Split large components
Naming
- PascalCase — Component names:
RevenueChart - kebab-case — File names:
revenue-chart.tsx - Consistency — Match names across config, exports, and functions
API Reference
Type Definitions
Support
- Documentation: docs.semaphor.cloud
- Email: support@semaphor.cloud
- GitHub Issues: Bug reports and feature requests
