# Building a Visualization
The visualization component is the core of your plugin. It receives structured props from the SDK and renders your custom UI.
# Component Signature
import type { CustomVisualization, CustomVisualizationProps } from '@sisense/sdk-ui';
import type { DataOptions, StyleOptions } from '../types';
type VisualizationProps = CustomVisualizationProps<DataOptions, StyleOptions>;
const Visualization: CustomVisualization<VisualizationProps> = (props) => {
return <div>...</div>;
};
# Props Overview
Your component receives VisualizationProps (i.e. CustomVisualizationProps<DataOptions, StyleOptions> — DataPoint defaults to AbstractDataPointWithEntries):
| Prop | Type | Description |
|---|---|---|
dataSource | DataSource | Data model to query. Pass to query hooks. |
dataOptions | DataOptions | Structured data config matching your data panel inputs. |
styleOptions | StyleOptions | Style config from the design panel. |
filters | Filter[] | FilterRelations | Active filters from the dashboard context. |
highlights | Filter[] | Cross-widget highlight filters. Matching data is visually emphasized. |
onDataPointClick | CustomVisualizationDataPointEventHandler | Single data point click. See Event Handling. |
onDataPointContextMenu | CustomVisualizationDataPointContextMenuHandler | Right-click handler. See Event Handling. |
onDataPointsSelected | CustomVisualizationDataPointsEventHandler | Multi-selection handler. See Event Handling. |
Both filters and highlights are passed to query hooks when fetching data (covered in Fetching Data).
Always define defaults for styleOptions so the widget renders correctly when no styles are configured:
const Visualization: CustomVisualization<VisualizationProps> = ({ styleOptions }) => {
const colorScheme = styleOptions?.colorScheme ?? 'warm';
const showLegend = styleOptions?.showLegend ?? true;
// ...
};
# Type Parameterization
CustomVisualizationProps accepts three type parameters:
CustomVisualizationProps<DataOptions, StyleOptions, DataPoint>;
| Parameter | Default | Description |
|---|---|---|
DataOptions | GenericDataOptions | Shape of data options matching data panel inputs |
StyleOptions | CustomVisualizationStyleOptions | Shape of style options from design panel |
DataPoint | AbstractDataPointWithEntries | Shape of data points in event handlers |
# Rendering Environment
Your component renders inside:
- WidgetContainer — title bar, description, export options, border styling
- DynamicSizeContainer — responsive width/height based on the widget's allocated space
- ErrorBoundary — catches rendering errors and shows fallback UI
Design your component to fill its container responsively. Handle expected errors gracefully:
const Visualization: CustomVisualization<VisualizationProps> = ({ dataOptions }) => {
if (!dataOptions.value?.length) {
return <div style={{ padding: 16, color: '#666' }}>Add a measure to get started.</div>;
}
return <div>...</div>;
};
# Widget Configuration
Control widget-level behavior via customWidget.config:
customWidget: {
name: 'my-custom-chart',
displayName: 'My Custom Chart',
config: {
header: {
visible: false, // Hide the widget header (title bar)
},
},
visualization: { Component: Visualization },
},
Set header.visible: false for full-bleed visualizations that don't need a title bar.
# Wrapping Built-In Charts
You can wrap an existing Compose SDK chart inside a plugin to reuse SDK rendering while customizing behavior:
import type { CustomVisualization, CustomVisualizationProps } from '@sisense/sdk-ui';
import { LineChart } from '@sisense/sdk-ui';
import type { DataOptions, StyleOptions } from '../types';
type MyCustomLineChartProps = CustomVisualizationProps<DataOptions, StyleOptions>;
export const MyCustomLineChart: CustomVisualization<MyCustomLineChartProps> = (props) => {
const styleOptions = { ...props.styleOptions };
return (
<LineChart
dataSet={props.dataSource}
dataOptions={props.dataOptions}
filters={props.filters}
styleOptions={styleOptions}
/>
);
};
When wrapping built-in charts:
StyledColumn[]andStyledMeasureColumn[]can be passed directly — no manual.map()toAttributeneeded; built-in charts accept styled columns- Spread
styleOptionsinto a new object so downstream style merging works correctly - Use
Pick<LineStyleOptions, ...>for yourStyleOptionsto expose only the properties your design panel controls
Next lesson: Fetching Data