Build charts in code with Luzmo Flex - ad-hoc, code-first visualizations with full slot/options control. Triggers on: "Flex chart", "build a chart", "chart not rendering", "0 height", "wrong component name", slot errors, slot configuration, "inlineData", "chart from my own data / without a dataset". Covers Flex sizing, framework-specific component names, unique contextId, localized strings, inline data (records instead of a dataset). Use eagerly for building Flex charts in code. Not for custom chart development (use custom-charts), embedding saved dashboards/charts by id (use core), chart editors (use analytics-studio), data connection (use data-integration), AI queries (use ai-analytics), or visual theming (use theming).
83%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Entry-point for building Flex charts in code in your application — ad-hoc, code-first visualizations with full slot/options control.
To embed an existing saved dashboard or chart by id, use core. To build a brand-new custom chart component when no built-in type fits, use custom-charts.
developer.luzmo.com is Luzmo's first-party, allowlisted documentation domain, maintained by the same publisher as this skill.search_docs tool is available, use it first to find the right pages: it returns matching passages with their live developer.luzmo.com or Academy URLs. Fetch the .md URL when you need the full page or anything newer than the server's docs build. Without MCP, go straight to the .md URLs below.https://developer.luzmo.com/.../*.md docs and their referenced URLs for implementation details.https://developer.luzmo.com/llms.txt and/or /llms-full.txt for discovery only.Read these rules BEFORE implementing ANY visualization — they prevent 80% of rendering failures:
Generate embed tokens server-side:
id (as authKey) and token (as authToken) passed to frontendIf ANY checkbox is unchecked, STOP and fix before proceeding.
For full auth/embed-token guidance, see core.
For localhost CORS or reconnecting /realtime sockets, see core/references/local-development-proxy.md: proxy /0.1.0 and /realtime through the app origin, set Flex apiHost to that origin, and keep appServer pointed directly at the Luzmo app host.
Using wrong names causes "component not found" errors:
| Framework | Flex Chart |
|---|---|
| Vanilla JS | <luzmo-embed-viz-item> |
| React | LuzmoVizItemComponent |
| Angular | <luzmo-viz-item> |
| Vue | <luzmo-viz-item> |
[ERROR] WRONG:
// Vanilla JS - missing "embed"
<luzmo-viz-item type="bar-chart" />[OK] CORRECT:
// Vanilla JS - includes "embed"
<luzmo-embed-viz-item type="bar-chart" />Without explicit dimensions → chart will be INVISIBLE (0 height)
Rule: Set dimensions on BOTH container AND component
┌─────────────────────────────────────────┐
│ Container div │ ◄── height: 400px;
│ (your wrapper element) │ width: 100%;
│ │
│ ┌──────────────────────────────────┐ │
│ │ <luzmo-embed-viz-item> │ │ ◄── height: 100%;
│ │ ↑ │ │ width: 100%;
│ │ Without BOTH dimensions set │ │
│ │ on the container AND the │ │
│ │ component, height collapses │ │
│ │ to 0 and the chart is invisible│ │
│ └──────────────────────────────────┘ │
│ │
└─────────────────────────────────────────┘[ERROR] WRONG - Renders nothing visible:
// Container has no height
<div>
<luzmo-embed-viz-item type="bar-chart" />
</div>
// Result: 0px height, invisible chart[OK] CORRECT - Set BOTH:
// Container with explicit size
<div style={{ height: '400px', width: '100%' }}>
<luzmo-embed-viz-item
type="bar-chart"
height="400px"
width="100%"
/>
</div>Example (wrong - chart will be invisible):
<div>
<luzmo-embed-viz-item type="bar-chart" ...></luzmo-embed-viz-item>
</div>contextId must be unique across all chart instances on the page. Reusing the same contextId causes conflicts and unexpected behavior.
Recommended pattern:
// Single chart
contextId="chart-1"
// Multiple charts - use a pattern
contextId={`chart-${dashboardId}-${itemId}`}
contextId={`${section}-${index}`}All user-facing text properties MUST be localized objects, never plain strings:
Properties that require localization:
titlelabel (in slots)descriptionCorrect:
{
label: { en: "Revenue" }
}Wrong:
{
label: "Revenue" // [ERROR] This will cause errors
}Avoid using the title option unless explicitly needed. If you do use it:
Correct:
options: {
title: { en: "My Chart Title" }
}Wrong:
options: {
title: "My Chart Title" // [ERROR] Not localized
}
// OR
options: {
title: { title: { en: "My Chart Title" } } // [ERROR] Nested, wrong structure
}Every dataset-backed Flex chart slot must follow these rules (for charts fed by inlineData, see "Inline Data" below — slots use field instead):
Always required:
datasetId (UUID of the dataset)Recommended:
label (localized object: { en: "Label" }) - any user-facing label you provide must be localizedColumn-based slots (dimensions and measures using columns):
columnId (UUID of the column)type (hierarchy, numeric, or datetime)Formula-based slots (measures using formulas):
formulaId (UUID of the formula) - when using this, do NOT include columnId, type, or aggregationFuncType-specific requirements:
| Column Type | Required Additional Field | Values |
|---|---|---|
numeric (measure) | aggregationFunc | sum, average, count, distinctcount, min, max, median, stddev, cumulativesum, histogram, rate, weightedaverage |
numeric (dimension) | bins | { enabled: true, number: 10 } for binning, or { enabled: false } to disable |
datetime (dimension) | level | 1=year, 2=quarter, 3=month, 4=week, 5=day, 6=hour, 7=minute, 8=second, 9=millisecond |
hierarchy (dimension) | level | 1=top level, 2=second level, etc. |
Special aggregation functions requiring a secondary column:
rate and weightedaverage require an additional aggregationWeight field that specifies the denominator or weight column:
{
datasetId: "...",
columnId: "...", // primary (numerator / value) column
label: { en: "Revenue per Unit" },
type: "numeric",
aggregationFunc: "weightedaverage", // or "rate"
aggregationWeight: {
datasetId: "...", // dataset of weight/denominator column
columnId: "..." // weight or denominator column id
}
}Optional but recommended:
subtype (if column has one): coordinates, currency, duration, hierarchy_element_expression, ip_address, topographyBefore configuring any chart: Consult the chart-specific documentation for available slots and options:
https://developer.luzmo.com/flex/charts/{chart-type}.mdhttps://developer.luzmo.com/guide/flex--chart-docs.mdhttps://developer.luzmo.com/assets/json-schemas/0.1.99/{chartType}-slots.schema.jsonhttps://developer.luzmo.com/assets/json-schemas/0.1.99/{chartType}-options.schema.jsonDifferent chart types have different required and optional properties; consult the schema (and any relevant guides it references) before guessing field shapes.
Since @luzmo/embed / @luzmo/react-embed / @luzmo/vue-embed 8.1.0, @luzmo/ngx-embed 10.1.0 and @luzmo/react-native-embed 7.1.0 (7.1.1 escapes quotes in props), a Flex viz item can render records you pass in through the inlineData property instead of querying a dataset. Source: the inlineData row in each package README and CHANGELOG (not yet on developer.luzmo.com; the public Flex JSON schemas 0.1.99 predate slot field).
| Your data | Use |
|---|---|
| Lives in a Luzmo dataset; chart should aggregate, filter, rank, or respect token filters | Dataset slots (datasetId + columnId/formulaId) |
| Already final, per-chart results computed by your backend (or another tool) | inlineData + slot field bindings |
Shape: inlineData = { records: [...], sourceDescription?: string, topojson?: ... }. Each populated slot content names its record property with field (no datasetId, columnId, formulaId):
const chart = document.querySelector('luzmo-embed-viz-item'); // type="bar-chart"
chart.slots = [
{ name: 'measure', content: [{ field: 'revenue', type: 'numeric', label: { en: 'Revenue' } }] },
{ name: 'y-axis', content: [{ field: 'product', type: 'hierarchy', label: { en: 'Product' } }] }
];
chart.inlineData = {
sourceDescription: 'Revenue per product from the billing service',
records: [
{ product: { id: 'analytics', name: { en: 'Analytics' } }, revenue: 124000 },
{ product: { id: 'embedded', name: { en: 'Embedded' } }, revenue: 81000 }
]
};React: <LuzmoVizItemComponent inlineData={...} />; Angular: [inlineData]; Vue: :inlineData.
Constraints (enforced by the chart; violations show the chart's error state):
records: [] renders "no data" (it does not fall back to a query).field, and fields must be unique across all slots.regular-table, pivot-table, custom-chart, venn-diagram, or filter types (date-filter, date-comparison-filter, dropdown-filter, search-filter, slicer-filter, slider-filter).luzmo-item-grid does not forward inlineData (ACK 1.0.8); render inline charts with the standalone viz item.[SECURITY] Inline records are treated as already authorized. Embed-token filters and parameter_overrides are NOT applied to them. Filter rows to what the viewer may see on your server before sending them to the browser.
Flex viz-items do not have a theme prop. The component API props are appServer, apiHost, authKey, authToken, type, slots, options, contextId, and optional interaction props. For standalone Flex viz-items, put chart styling in options (for example options.theme, options.color, or chart-specific options after fetching the chart schema).
When using dark chart styling, the chart content can adapt, but the container background does not.
Required: Set an explicit dark background on the container element.
Example:
<div style="background-color: #1a1a2e;">
<luzmo-embed-viz-item id="sales-chart" type="bar-chart"></luzmo-embed-viz-item>
</div>document.querySelector('#sales-chart').options = {
theme: {
itemsBackground: '#1a1a2e',
colors: ['#FFB74D', '#FFEB3B', '#FF4081'],
font: { fontFamily: 'Inter, system-ui, sans-serif', fontSize: 13 },
},
}references/flex-charts.md — Flex chart embedding. Covers code-first chart creation, slot/options configuration, chart-type catalog, JSON schema access, and advanced slot patterns (formulas, bins, datetime levels).If your visualization isn't working as expected, check these common issues:
| Problem | Likely Cause | Solution |
|---|---|---|
| Chart is invisible (0 height) | Missing dimensions on Flex chart | Set height and width on BOTH container and component |
| "Invalid label" or title errors | Non-localized strings | Change "text" to { en: "text" } for all user-facing properties |
| Charts showing wrong data | Duplicate contextId | Make each contextId unique across the page |
| Dark theme looks broken | Missing container background | Set explicit dark background-color on container element |
| Component not loading at all | Wrong component name | Check component naming table above for your framework |
| Slot configuration errors | Missing type or aggregationFunc | Review slot configuration requirements above |
| Inline-data chart shows an error | Missing/duplicate field, records not an array, unsupported chart type, period-over-period set, or payload > 2 MB | Check the "Inline Data" constraints above |
inlineData ignored, chart still queries | Embed package older than 8.1.0 (ngx 10.1.0) | Upgrade the embed package |
Each pitfall below includes the error you'll see, why it fails, a frequency marker ([WARNING] VERY COMMON / [WARNING] COMMON / [WARNING] OCCASIONAL), and where to escalate if you need deeper troubleshooting.
[ERROR] Using plain strings instead of localized objects ([WARNING] VERY COMMON):
label: "Revenue" // WrongYou'll see: Invalid label / Invalid title / Invalid localized string format.
Why this fails: Luzmo requires every user-facing text field to be a language object so it can handle i18n consistently. A bare string has no language tag, so the validator rejects it.
[OK] Always use localized objects:
label: { en: "Revenue" } // CorrectSee also: troubleshooting → "Title and Label Errors".
[ERROR] Reusing the same contextId ([WARNING] COMMON):
<luzmo-embed-viz-item contextId="chart-1" />
<luzmo-embed-viz-item contextId="chart-1" /> // Conflict!You'll see: charts swapping data on reload, filter changes affecting the wrong chart, stale data not refreshing.
Why this fails: contextId is the key Luzmo uses to deduplicate state and queries across chart instances. Two charts sharing one id share state — chaos follows.
[OK] Make each contextId unique:
<luzmo-embed-viz-item contextId="chart-1" />
<luzmo-embed-viz-item contextId="chart-2" /> // GoodSee also: troubleshooting → "Chart Rendering But Shows Wrong Data".
[ERROR] Forgetting dimensions on Flex charts ([WARNING] VERY COMMON — most common failure mode):
<luzmo-embed-viz-item type="bar-chart" /> // InvisibleYou'll see: nothing on the page. No error. DevTools shows the element with 0px height. Why this fails: Flex charts use their container's box for layout — without explicit dimensions on BOTH container and component, the chart computes 0px and renders invisibly. [OK] Always set height and width on BOTH:
<div style="height: 400px; width: 100%;">
<luzmo-embed-viz-item style="height: 100%; width: 100%;" />
</div>See also: troubleshooting → "Component Not Rendering At All".
[ERROR] Missing required slot fields ([WARNING] COMMON):
{
datasetId: "...",
columnId: "..."
// Missing: label, type, aggregationFunc
}You'll see: Missing aggregationFunc (for numeric measures), Missing level (for datetime/hierarchy), or Invalid label.
Why this fails: Slots are typed configurations — different column types need different bookkeeping. A numeric measure without aggregationFunc is ambiguous; a datetime without level can't be bucketed.
[OK] Include all required fields:
{
datasetId: "...",
columnId: "...",
label: { en: "Total Sales" },
type: "numeric",
aggregationFunc: "sum"
}See also: troubleshooting → "Slot Configuration Errors" and the chart-specific doc at https://developer.luzmo.com/flex/charts/{chart-type}.md.
[ERROR] Treating inlineData like a dataset ([WARNING] COMMON):
chart.slots = [{ name: 'measure', content: [{ datasetId: '...', columnId: '...', field: 'revenue', aggregationFunc: 'sum' }] }];
chart.inlineData = { records: rawTransactions }; // per-transaction rows, all tenantsYou'll see: one bar per raw row instead of totals, rows from other tenants visible, or a slot-binding error.
Why this fails: Inline records skip the query engine, so nothing is aggregated or filtered, and token filters never touch them.
[OK] Send final, already-filtered results and bind by field only:
chart.slots = [{ name: 'measure', content: [{ field: 'revenue', type: 'numeric', label: { en: 'Revenue' } }] }, /* ... */];
chart.inlineData = { records: totalsForThisViewer }; // aggregated + filtered server-side[ERROR] Using deprecated <cumul-*> tag names ([WARNING] OCCASIONAL but increasing):
<luzmo-viz-item type="bar-chart" ...></luzmo-viz-item> <!-- Wrong: missing "embed" in vanilla JS -->You'll see: Component not found in console, or the element renders as inert markup.
Why this fails: The product rebranded from Cumul.io to Luzmo and the tag/component names changed.
[OK] Use the current Luzmo names:
<luzmo-embed-viz-item type="bar-chart" ...></luzmo-embed-viz-item>contextId across multiple chart instances on the same page.core.inlineData — token filters do not apply to inline records.When to escalate to other skills:
corecoremultitenancy (SECURITY CRITICAL)analytics-studiothemingdata-integrationai-analyticscustom-chartstroubleshooting FIRST, then return here for the fixThis skill does NOT cover:
core)core)analytics-studio)resource-management)https://developer.luzmo.com/llms.txt, https://developer.luzmo.com/llms-full.txthttps://developer.luzmo.com/api/{action}{Resource}.mdhttps://developer.luzmo.com/guide/*.mdhttps://developer.luzmo.com/flex/charts/{type}.mdIf content exists on developer.luzmo.com, link — do not duplicate specs here.
luzmo-official/agent-skills
7dc2a09
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.