This guide shows you how to create charts and visualizations from query results using Lightdash data models. For detailed API documentation, see Charts API.
Lightdash provides data models that transform query results into ECharts configurations for visualization:
Cartesian charts display data on X and Y axes (bar, line, area, scatter).
import {
CartesianChartDataModel,
ChartKind,
type MetricQuery,
type Explore,
} from "@lightdash/common";
// Create data model
const dataModel = new CartesianChartDataModel({
resultsRunner,
fieldConfig: {
x: {
reference: "orders_created_month",
type: "category",
},
y: [{
reference: "orders_total_revenue",
aggregation: "sum",
}],
},
type: ChartKind.VERTICAL_BAR,
});
// Get ECharts options
const echartsOptions = dataModel.getChartOptions();
// Use with ECharts library (in frontend code)
// const chart = echarts.init(document.getElementById('chart'));
// chart.setOption(echartsOptions);const dataModel = new CartesianChartDataModel({
resultsRunner,
fieldConfig: {
x: {
reference: "orders_created_date",
type: "time",
},
y: [
{
reference: "orders_total_revenue",
aggregation: "sum",
},
{
reference: "orders_count",
aggregation: "count",
},
],
},
type: ChartKind.LINE,
});Display data grouped by a dimension:
const dataModel = new CartesianChartDataModel({
resultsRunner,
fieldConfig: {
x: {
reference: "orders_created_month",
type: "category",
},
y: [{
reference: "orders_total_amount",
aggregation: "sum",
}],
groupBy: {
reference: "orders_status",
type: "category",
},
},
type: ChartKind.VERTICAL_BAR,
});const dataModel = new CartesianChartDataModel({
resultsRunner,
fieldConfig: {
x: {
reference: "orders_total_amount",
type: "linear",
},
y: [{
reference: "orders_item_count",
aggregation: "sum",
}],
},
type: ChartKind.SCATTER,
});const dataModel = new CartesianChartDataModel({
resultsRunner,
fieldConfig: {
x: {
reference: "orders_created_date",
type: "time",
},
y: [{
reference: "orders_total_revenue",
aggregation: "sum",
}],
},
type: ChartKind.AREA,
config: {
layout: {
showGridX: true,
showGridY: true,
},
eChartsConfig: {
series: [{
areaStyle: { opacity: 0.3 },
}],
},
},
});Display proportional data as circular charts.
import { PieChartDataModel, ChartKind } from "@lightdash/common";
const dataModel = new PieChartDataModel({
resultsRunner,
fieldConfig: {
dimension: "orders_status",
metric: "orders_count",
},
type: ChartKind.PIE,
});
const echartsOptions = dataModel.getChartOptions();const dataModel = new PieChartDataModel({
resultsRunner,
fieldConfig: {
dimension: "orders_region",
metric: "orders_total_revenue",
},
type: ChartKind.PIE,
config: {
layout: {
isDonut: true,
},
},
});Display data in tabular format.
import { TableDataModel } from "@lightdash/common";
const dataModel = new TableDataModel({
resultsRunner,
columnConfig: [
{ fieldId: "customers_name", visible: true },
{ fieldId: "orders_count", visible: true },
{ fieldId: "orders_total_revenue", visible: true },
],
conditionalFormatting: [],
});
const tableData = dataModel.getTransformedData();import {
TableDataModel,
type ConditionalFormattingConfig,
} from "@lightdash/common";
const conditionalFormatting: ConditionalFormattingConfig[] = [{
target: { fieldId: "orders_total_revenue" },
rule: {
type: "greater_than",
value: 10000,
},
color: "#4CAF50",
}];
const dataModel = new TableDataModel({
resultsRunner,
columnConfig: columns,
conditionalFormatting,
});const dataModel = new CartesianChartDataModel({
resultsRunner,
fieldConfig,
type: ChartKind.VERTICAL_BAR,
config: {
layout: {
xLabel: "Month",
yLabel: ["Revenue ($)"],
showGridX: false,
showGridY: true,
showLegend: true,
},
},
});const dataModel = new CartesianChartDataModel({
resultsRunner,
fieldConfig,
type: ChartKind.LINE,
config: {
eChartsConfig: {
color: ["#FF6B6B", "#4ECDC4", "#45B7D1"],
},
},
});const dataModel = new CartesianChartDataModel({
resultsRunner,
fieldConfig,
type: ChartKind.VERTICAL_BAR,
config: {
eChartsConfig: {
xAxis: {
axisLabel: {
rotate: 45,
},
},
yAxis: {
scale: true,
min: 0,
},
},
},
});Transform data before charting:
import { derivePivotConfigurationFromChart, type PivotConfig } from "@lightdash/common";
// Derive pivot configuration from chart
const pivotConfig: PivotConfig | undefined = derivePivotConfigurationFromChart(
chart,
query,
itemsMap
);
if (pivotConfig) {
// Apply pivot transformation
const pivotedData = pivotQueryResults({
rows,
...pivotConfig,
itemsMap,
});
}Get transformed data for custom rendering:
const dataModel = new CartesianChartDataModel({
resultsRunner,
fieldConfig,
type: ChartKind.LINE,
});
// Get pivoted data
const pivotedData = dataModel.getPivotedChartData();
// Get fully transformed data
const transformedData = dataModel.getTransformedData();Apply color rules to chart data:
import {
createConditionalFormattingConfigWithColorRange,
type ConditionalFormattingColorRange,
} from "@lightdash/common";
const colorRange: ConditionalFormattingColorRange = {
min: { value: 0, color: "#FF0000" },
max: { value: 100, color: "#00FF00" },
};
const config = createConditionalFormattingConfigWithColorRange(
colorRange,
{ fieldId: "orders_total_revenue" }
);VERTICAL_BAR - Vertical bar chartHORIZONTAL_BAR - Horizontal bar chartLINE - Line chartAREA - Area chartSCATTER - Scatter plotMIXED - Mixed chart (multiple series with different types)PIE - Pie or donut chartTABLE - Data tableBIG_NUMBER - Single metric displayFUNNEL - Funnel chartEnd-to-end chart creation:
import {
CartesianChartDataModel,
ChartKind,
getItemMap,
formatRows,
type MetricQuery,
type MetricQueryResponse,
} from "@lightdash/common";
async function createRevenueChart(
query: MetricQuery,
response: MetricQueryResponse,
explore: Explore
) {
// 1. Create item map
const itemsMap = getItemMap(
explore,
query.additionalMetrics,
query.tableCalculations,
query.customDimensions
);
// 2. Format results
const formattedRows = formatRows(response.rows, itemsMap);
// 3. Create results runner
const resultsRunner = {
rows: formattedRows,
columns: query.dimensions.concat(query.metrics),
};
// 4. Create data model
const dataModel = new CartesianChartDataModel({
resultsRunner,
fieldConfig: {
x: {
reference: "orders_created_month",
type: "category",
},
y: [{
reference: "orders_total_revenue",
aggregation: "sum",
}],
},
type: ChartKind.VERTICAL_BAR,
config: {
layout: {
xLabel: "Month",
yLabel: ["Total Revenue"],
showLegend: false,
},
eChartsConfig: {
color: ["#4ECDC4"],
},
},
});
// 5. Get ECharts options
const chartOptions = dataModel.getChartOptions();
return chartOptions;
}Keep charts readable by limiting the number of series:
// Good: 2-3 series
y: [
{ reference: "revenue" },
{ reference: "cost" },
]
// Avoid: Too many series
y: metrics.map(m => ({ reference: m })) // Could be 10+ seriesUse appropriate time granularity:
// For trends: daily or weekly
x: { reference: "orders_created_date", type: "time" }
// For overview: monthly or quarterly
x: { reference: "orders_created_month", type: "category" }Make labels readable:
config: {
layout: {
xLabel: "Order Date",
yLabel: ["Revenue (USD)"],
},
eChartsConfig: {
xAxis: {
axisLabel: {
rotate: 45, // Prevent label overlap
},
},
},
}