@microsoft/fabric-visuals
v1.3.0
Published
Vega-Lite visualization component for Fabric Apps - Analytics
Downloads
6,908
Readme
@microsoft/fabric-visuals
Vega-Lite visualization component for Fabric Apps - Analytics
Quick Reference
Package:
@microsoft/fabric-visualsPurpose: React component for rendering Vega-Lite visualizations in Fabric Apps. Use when: You need to render charts (bar, line, area, scatter, etc.) from aDataTableusing Vega-Lite specs. Do NOT use when: You need a data grid/table (use@microsoft/fabric-datagrid), or you only need shared types/tokens (use@microsoft/fabric-visuals-core). Key exports:VegaVisual,VegaVisualProps,VegaVisualHandle,useCssThemeCSP-safe entry:@microsoft/fabric-visuals/embeddable— exportsEmbeddableVegaVisualfor sandboxed/iframe environments Peer dependencies:@microsoft/fabric-visuals-coreInstall:npm install @microsoft/fabric-visuals @microsoft/fabric-visuals-core
Ecosystem Context
@microsoft/fabric-visualsdepends on@microsoft/fabric-visuals-corefor sharedDataTable,ColumnDef, andVisualThemecontracts, plus design-token-backed theme values.- It is the charting sibling to
@microsoft/fabric-datagrid, which renders the sameDataTableshape as a table/grid instead of a chart. - In Fabric Apps - Analytics, the data passed to
VegaVisualtypically comes from@microsoft/fabric-app-dataqueries, then gets mapped into aDataTable.
Installation
npm install @microsoft/fabric-visuals @microsoft/fabric-visuals-core@microsoft/fabric-visuals-core is required alongside this package because the public API uses its DataTable and VisualTheme types.
Public API
The package entry point exports only the symbols below.
React exports
export interface VegaVisualProps {
spec: VisualizationSpec | string;
theme: VisualTheme;
data?: DataTable | null;
configVegaLite?: VegaLiteConfig;
capabilities?: VegaVisualCapabilities;
onEvent?: VegaVisualEventCallback;
onInteraction?: InteractionEventCallback;
className?: string;
style?: CSSProperties;
}
export interface VegaVisualHandle {
readonly view: View | null;
}
export const VegaVisual: React.ForwardRefExoticComponent<
VegaVisualProps & React.RefAttributes<VegaVisualHandle>
>;| Export | Signature | Notes |
| --- | --- | --- |
| VegaVisual | React.ForwardRefExoticComponent<VegaVisualProps & React.RefAttributes<VegaVisualHandle>> | Responsive SVG Vega/Vega-Lite renderer. Injects DataTable data, compiles Vega-Lite, and exposes the underlying Vega View through a ref. |
| VegaVisualProps | interface above | spec accepts a parsed Vega-Lite object or a JSON string. configVegaLite merges on top of the built-in base theme. onInteraction emits selection/clear events for host-driven cross-highlight. |
| VegaVisualHandle | interface above | ref.current?.view gives direct access to the current Vega View. |
Theme hook
export function useCssTheme(): VisualTheme;Reads the current CSS custom properties via @microsoft/fabric-visuals-core and returns a live VisualTheme that updates when the app theme changes.
Types and constants
export type VisualizationSpec = VegaLiteSpec;
export type VegaLiteConfig = VlConfig;
export type LogLevelName = 'error' | 'warn' | 'info' | 'debug' | 'none';
export const LOG_LEVEL_MAP: Record<LogLevelName, number>;
export type VegaVisualEventType = 'render' | 'error' | 'vega' | 'specUpdate';
export type VegaVisualEventLevel = Exclude<LogLevelName, 'none'>;
export interface VegaVisualEventDetail {
readonly type: VegaVisualEventType;
readonly level: VegaVisualEventLevel;
readonly message: string;
readonly spec?: string;
readonly args?: readonly unknown[];
}
export type VegaVisualEventCallback =
(event: CustomEvent<VegaVisualEventDetail>) => void;| Export | Signature | Notes |
| --- | --- | --- |
| VisualizationSpec | VegaLiteSpec | Alias for Vega-Lite top-level specs. |
| VegaLiteConfig | VlConfig | Alias for Vega-Lite config objects. |
| LogLevelName | 'error' \| 'warn' \| 'info' \| 'debug' \| 'none' | Friendly log-level names. |
| LOG_LEVEL_MAP | Record<LogLevelName, number> | Maps friendly names to Vega numeric log levels. |
| VegaVisualEventType | 'render' \| 'error' \| 'vega' \| 'specUpdate' | Unified event subtypes. |
| VegaVisualEventLevel | Exclude<LogLevelName, 'none'> | Event levels emitted by VegaVisual. |
| VegaVisualEventDetail | interface above | Payload for all onEvent callbacks. |
| VegaVisualEventCallback | (event: CustomEvent<VegaVisualEventDetail>) => void | Single callback shape for render, error, and Vega log events. |
Internal theme/color utilities
vegaLiteBaseTheme, themeToVegaConfig, compileSpec, applyColumnFormats, and generateColorPalette(baseColors: readonly string[], sectors = 12): string[] exist in src/, but they are not exported from the package entry point.
Capabilities
VegaVisual applies a set of optimistic transforms during spec compilation. The optional capabilities prop lets consumers selectively disable these behaviors by passing a partial VegaVisualCapabilities object. All flags default to false (behavior enabled); set a flag to true to disable it.
Interface
import type { VegaVisualCapabilities } from '@microsoft/fabric-visuals';
interface VegaVisualCapabilities {
disableStackedDataLabels?: boolean;
disableNiceAxisBounds?: boolean;
disableLineChartCrosshairTooltip?: boolean;
disableTextTruncation?: boolean;
disableLegendTruncation?: boolean;
disableLegendScroll?: boolean;
disableCategoricalScroll?: boolean;
disableMinBarSize?: boolean;
disableDynamicAxisLabelOverlap?: boolean;
disableNonZeroQuantitativeBaseline?: boolean;
disablePointMarkClip?: boolean;
disableSelfHighlight?: boolean;
}Capability flags
| Flag | Default | Effect when true |
|------|---------|-------------------|
| disableStackedDataLabels | false | Skips adding centered white text labels on eligible stacked bar charts. |
| disableNiceAxisBounds | false | Skips setting scale.nice = 5 on quantitative axes for cleaner tick values. |
| disableLineChartCrosshairTooltip | false | Skips adding the vertical rule + highlighted points on line/area charts. |
| disableTextTruncation | false | Skips truncating long text marks to prevent overflow. |
| disableLegendTruncation | false | Skips capping legend entry count (symbolLimit) based on chart dimensions. Only applies when legend scrolling is disabled. |
| disableLegendScroll | true | Skips extracting legends into scrollable HTML. When disabled, legends stay in SVG. |
| disableCategoricalScroll | false | Skips making bar/column charts scrollable when categories overflow. |
| disableMinBarSize | false | Skips enforcing a minimum bar thickness for small values. |
| disableDynamicAxisLabelOverlap | false | Skips rotation-aware labelOverlap assignment (rotated → no removal, horizontal → parity). |
| disableNonZeroQuantitativeBaseline | false | Skips allowing non-zero baselines for non-stacked, quantitative measures. |
| disablePointMarkClip | false | Skips clipping point-based charts (scatter and bubble) to the plot area, so points at the extremes may overflow past the axes. |
| disableSelfHighlight | false | Skips injecting native self-highlight (point selection param + conditional opacity) that dims unselected marks on click; Shift/Ctrl/Cmd-click adds to the selection. |
Usage
import { VegaVisual, useCssTheme } from '@microsoft/fabric-visuals';
import type { VegaVisualCapabilities } from '@microsoft/fabric-visuals';
const capabilities: VegaVisualCapabilities = {
disableCategoricalScroll: true,
disableMinBarSize: true,
};
export function Example() {
const theme = useCssTheme();
return (
<VegaVisual
spec={spec}
data={data}
theme={theme}
capabilities={capabilities}
/>
);
}Omitted keys keep their default behavior. The capabilities object is shallowly merged with the defaults at render time — you only need to specify the flags you want to change.
Key Constraints & Gotchas
DO NOT put config in Vega-Lite spec JSON files
VegaVisual always applies its built-in theme. Custom config in the spec is overridden.
// ✅ DO: Pass overrides via configVegaLite prop
<VegaVisual spec={spec} theme={theme} configVegaLite={{ axis: { labelAngle: -45 } }} />
// ❌ DON'T: Put config inside the Vega-Lite spec
const spec = { mark: "bar", config: { axis: { labelAngle: -45 } }, ... }; // config is ignoredClean legend labels at the data level, not with labelExpr
VegaVisual renders its own custom legend UI and ignores Vega-Lite labelExpr.
// ✅ DO: Use a calculate transform to clean values
{ "calculate": "split(datum['Category[Name]'], ']')[0]", "as": "CleanCategory" }
// ❌ DON'T: Use labelExpr in encoding — it will be ignored
{ "encoding": { "color": { "legend": { "labelExpr": "split(datum.label, ']')[0]" } } } }Hide a legend with legend: null
To blank a legend, set legend: null on the encoding channel (e.g. the size channel of a bubble chart). This is honored even when another channel encodes the same field. VegaVisual auto-applies column formats to legends, but it will not resurrect a legend you explicitly suppressed with legend: null.
// ✅ DO: Suppress the size legend explicitly — the merged legend stays hidden
{ "encoding": { "size": { "field": "Score", "legend": null }, "color": { "field": "Score" } } }
// ❌ DON'T: Omit legend and expect no legend — a formatted, merged legend may appear
{ "encoding": { "size": { "field": "Score" }, "color": { "field": "Score" } } }Bracket escaping differs between encoding and calculate
// ✅ DO: Use \\[ in encoding field references
{ "encoding": { "x": { "field": "Date\\[MonthName\\]" } } }
// ✅ DO: Use raw brackets in calculate expressions
{ "calculate": "datum['Date[MonthName]']", "as": "Month" }
// ❌ DON'T: Use raw brackets in encoding fields
{ "encoding": { "x": { "field": "Date[MonthName]" } } }
// ❌ DON'T: Use \\[ escaping in calculate expressions
{ "calculate": "datum['Date\\[MonthName\\]']", "as": "Month" }Dual Y-axis charts need nested layers
// ✅ DO: Wrap each series in its own layer
{
"resolve": { "scale": { "y": "independent" } },
"layer": [
{ "layer": [{ "mark": "line", ... }, { "mark": "point", ... }] },
{ "layer": [{ "mark": "line", ... }, { "mark": "point", ... }] }
]
}Minimal Usage Examples
Basic component usage
import { VegaVisual, useCssTheme } from '@microsoft/fabric-visuals';
import type { VisualizationSpec } from '@microsoft/fabric-visuals';
import type { DataTable } from '@microsoft/fabric-visuals-core';
const spec: VisualizationSpec = {
$schema: 'https://vega.github.io/schema/vega-lite/v6.json',
mark: 'bar',
encoding: {
x: { field: 'category', type: 'nominal', title: 'Category' },
y: { field: 'value', type: 'quantitative', title: 'Value' },
},
};
const data: DataTable = {
columns: [
{ name: 'category', displayName: 'Category' },
{ name: 'value', displayName: 'Value', format: '#,##0' },
],
rows: [
['A', 10],
['B', 25],
['C', 18],
],
};
export function Example() {
const theme = useCssTheme();
return (
<VegaVisual
spec={spec}
data={data}
theme={theme}
style={{ height: 320 }}
/>
);
}Embeddable (CSP-Safe) Variant
For environments with restrictive Content Security Policy (sandboxed iframes, M365 Copilot widgets, VS Code MCP Apps), use EmbeddableVegaVisual from the ./embeddable entry point.
Why
VegaVisual relies on Vega's default expression compiler which uses new Function(). This is blocked in CSP environments that disallow unsafe-eval. When VegaVisual detects this restriction, it renders an error message instead of a blank chart.
EmbeddableVegaVisual uses vega-interpreter for expression evaluation — a CSP-safe alternative that interprets expression ASTs at runtime without code generation.
Installation
npm install @microsoft/fabric-visuals @microsoft/fabric-visuals-coreUsage
import { EmbeddableVegaVisual, useCssTheme } from '@microsoft/fabric-visuals/embeddable';
import type { VisualizationSpec } from '@microsoft/fabric-visuals/embeddable';
export function SecureWidget() {
const theme = useCssTheme();
return (
<EmbeddableVegaVisual
spec={spec}
data={data}
theme={theme}
style={{ height: 320 }}
/>
);
}API
EmbeddableVegaVisual accepts the same props as VegaVisual (VegaVisualProps) and exposes the same VegaVisualHandle ref. It is a drop-in replacement — switch the import path and component name.
The ./embeddable entry re-exports all types from the main entry (VisualizationSpec, VegaVisualEventCallback, VegaVisualProps, etc.) plus useCssTheme, so you only need one import path.
When to use which
| Environment | Component | Import path |
|---|---|---|
| Standard web app, no CSP restrictions | VegaVisual | @microsoft/fabric-visuals |
| Sandboxed iframe, Copilot widget, MCP App | EmbeddableVegaVisual | @microsoft/fabric-visuals/embeddable |
| Unsure / mixed deployment | EmbeddableVegaVisual | @microsoft/fabric-visuals/embeddable |
Note:
EmbeddableVegaVisualworks in all environments (CSP or not). If you are unsure whether your target environment enforces CSP, it is safe to always use the embeddable variant. The tradeoff is a slightly larger bundle size (~6 KB gzipped forvega-interpreter).
Tradeoffs
| | VegaVisual | EmbeddableVegaVisual |
|---|---|---|
| CSP-safe | ❌ Requires unsafe-eval | ✅ No unsafe-eval needed |
| Bundle size | Smaller | +~6 KB gzipped |
| Expression performance | Native (compiled) | Interpreted (negligible for typical charts) |
| API surface | Full | Identical |
Trademarks
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.
Security
Microsoft takes the security of our software products and services seriously, which includes all source code repositories in our GitHub organizations.
Please do not report security vulnerabilities through public GitHub issues.
For security reporting information, locations, contact information, and policies, please review the latest guidance for Microsoft repositories at https://aka.ms/SECURITY.md.
Code of conduct
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
