Embedded Analytics Without the Data Wrangling
Building data visualizations usually means wrestling with API responses until they fit whatever shape your chart library demands. Luzmo Flex, a React component from the embedded analytics platform Luzmo, takes a different route: it lets you query and render data through code while reusing the platform's built-in capabilities for multi-tenancy, localization, and interactivity.
The component, LuzmoVizItemComponent, connects directly to datasets you define in Luzmo. You can pull from APIs like Google Analytics, query a PostgreSQL database, or upload a .csv file — then configure everything from data queries to visual presentation in code.
What distinguishes Flex from typical charting libraries is that the core analytics machinery — dataset management, permission-based data isolation, language and currency formatting — comes ready-made. You're not rebuilding those layers yourself; you're composing visualizations on top of them.
Flex vs. a Charting Library
Consider a common scenario: fetching the top three page views from the last seven days via the Google Analytics API. The raw response returns nested arrays of dimension and metric values:
[
{
"dimensionValues": [
{
"value": "www.paulie.dev/",
"oneValue": "value"
},
{
"value": "Paul Scanlon | Home",
"oneValue": "value"
}
],
"metricValues": [
{
"value": "61",
"oneValue": "value"
}
]
},
{
"dimensionValues": [
{
"value": "www.paulie.dev/posts/2023/11/a-set-of-sign-in-with-google-buttons-made-with-tailwind/",
"oneValue": "value"
},
{
"value": "Paul Scanlon | A set of: \"Sign In With Google\" Buttons Made With Tailwind",
"oneValue": "value"
}
],
"metricValues": [
{
"value": "41",
"oneValue": "value"
}
]
},
{
"dimensionValues": [
{
"value": "www.paulie.dev/posts/2023/10/what-is-a-proxy-redirect/",
"oneValue": "value"
},
{
"value": "Paul Scanlon | What Is a Proxy Redirect?",
"oneValue": "value"
}
],
"metricValues": [
{
"value": "23",
"oneValue": "value"
}
]
}
]
To render that with a library like Recharts, the data must be reshaped into an array of objects with name and value properties. That means iterating with Array.prototype.map(), destructuring each row, and constructing new key-value pairs:
const data = response.rows.map((row) => {
const { dimensionValues, metricValues } = row;
const pageTitle = dimensionValues[1].value;
const totalUsers = parseInt(metricValues[0].value);
return {
name: pageTitle,
value: totalUsers,
};
});
That transformation code needs unit tests to guard against regressions — all before any chart work begins. Luzmo Flex accepts the dataset as-is, so the intermediate reformatting step disappears entirely.
When to Use Luzmo Flex
Flex suits applications that need more than a static dashboard. Because it supports event listeners and integrates with non-Luzmo components, you can build interactive data products — for instance:
- A report builder that lets users pick a chart type and filter the underlying dataset.
- A filter panel using plain HTML
Selectinputs that update every visualization on the page. - A wearables dashboard pulling personal metrics into a custom UI.
For simple, self-contained charts where you're comfortable managing data shape yourself, a lightweight library may still be the right call. Flex is aimed at products where data sourcing, permissions, and cross-component interactivity are first-class concerns.
Building a Google Analytics Data Product
The rest of this walkthrough builds a small data product: three charts showing different Google Analytics reporting dimensions for page views over the last seven days. All code is available in the luzmo-flex-tutorial repository.
Start by signing up for a free Luzmo trial, then follow either the Next.js starter guide or the Astro starter guide. This example uses the Next.js version.
Creating the Dataset
In the Luzmo dashboard, go to Datasets and choose GA4 Google Analytics. Follow the connection flow to link your Google Analytics account, then pick Custom selection when choosing reporting dimensions.
Search for and select these four fields:
- Device Category
- Page Title
- Date
- Total users
Click Import to finalize the dataset. You'll need its ID from the browser's URL bar in a later step.
Assuming you've completed one of the starter guides, your .env file already contains the API Key, API Token, App server, and API host variables.
Installing Dependencies
If you cloned a starter repository, install its dependencies first:
npm install
Then add the Luzmo React Embed package, which exports LuzmoVizItemComponent:
npm install @luzmo/react-embed@latest
Open page.tsx in the src/app directory and add your dataset id. Destructure the access object from the API response and pass access.datasets[0].id to the LuzmoClientComponent via a prop named datasetId:
// src/app/page.tsx
+ import dynamic from 'next/dynamic';
import Luzmo from '@luzmo/nodejs-sdk';
- import LuzmoClientComponent from './components/luzmo-client-component';
+ const LuzmoClientComponent = dynamic(() => import('./components/luzmo-client-component'), {
ssr: false,
});
const client = new Luzmo({
api_key: process.env.LUZMO_API_KEY!,
api_token: process.env.LUZMO_API_TOKEN!,
host: process.env.NEXT_PUBLIC_LUZMO_API_HOST!,
});
export default async function Home() {
const response = await client.create('authorization', {
type: 'embed',
username: 'user id',
name: 'first name last name',
email: '[email protected]',
access: {
datasets: [
{
- id: '<dataset_id>',
+ id: '42b43db3-24b2-45e7-98c5-3fcdef20b1a3',
rights: 'use',
},
],
},
});
- const { id, token } = response;
+ const { id, token, access } = response;
- return <LuzmoClientComponent authKey={id} authToken={token} />;
+ return <LuzmoClientComponent authKey={id} authToken={token} datasetId={access.datasets[0].id} />;
}
The final step is editing luzmo-client-component.tsx in src/app/components — that's where the chart definitions live.
Column IDs in Luzmo Flex
Every chart configuration in Luzmo Flex — whether it’s a filter parameter, measure, or category — requires a column ID from your dataset. The key name differs by context: it’s column_id inside filter parameters, and simply column inside measure and category objects. In both cases, the value is the dataset’s column ID.
To find these IDs, open your dataset in the Luzmo dashboard. Click the “more dots” menu next to a column heading and choose Copy column id. Paste that value into the appropriate configuration key. In the examples that follow, Total users serves as the measure, Device category as the category, and Date as the filter.
Donut Chart Configuration
The first example renders a donut chart showing visitor device distribution. The component’s type prop is set to donut-chart; other supported values include area-chart, bar-chart, bubble-chart, box-plot, and more (see the Luzmo Chart docs for the full list). The chart component requires an explicit height wrapper — Tailwind classes like w-1/2 and h-80 work well since LuzmoVizItemComponent defaults to 100% height, which would collapse to zero inside a heightless parent.
The options object customizes appearance and accepts configurations for:
- A
titlemap with locale-specific display text. - A
display titleflag controlling title visibility. - A
modethat switches between donut and pie rendering. - A
legendposition.
Full options are documented in the Donut chart reference.
Slots define which dataset columns populate the chart. Each slot can hold multiple measures, though when more than two are provided, one becomes the measure. Individual measure items contain a content array with a label, a column ID, a datasetId, a data type, and a format string. The format uses Python floating-point syntax (similar to JavaScript’s .toFixed()). The hierarchy type is Luzmo’s standard for any text column.
Filters restrict the displayed data. In this example, a filter keeps only records from the last seven days by comparing the Date column against an ISO date string (for instance, 2024-08-21T14:25:40.088Z) using a Luzmo Filter Expression that checks whether each row’s date is greater than or equal to the cutoff.
Line Chart Configuration
The second chart plots page views per date for the last seven days. The initial props match the donut chart except the type becomes line-chart. The options follow the same pattern, with mode set to line-chart.
The slots differ in two ways: the date column replaces the device category, and the slot type becomes x-axis instead of category. A level of 5 ensures daily granularity in the date formatting — see the Luzmo documentation for details on levels.
The filter logic is identical to the donut chart.
Bar Chart Configuration
The final chart ranks the top ten most-viewed pages by page-view count. Again, the props stay the same aside from setting type to bar-chart.
The options add several enhancements beyond the earlier charts: border-radii styling for the bars, a sort directive that orders by measure descending, and a limit of 10 results. For slots, the chart uses the y-axis slot type with the page title column. Filters match the previous charts.
Making Charts User-Configurable
Because each chart is an ordinary React component, you can turn them into configurable UI elements. Allow end users to toggle options like measure, category, or date range via standard HTML inputs — checkboxes, selects, and date pickers — and feed those values straight into the chart props. The real advantage is that you never mutate the underlying data.
This matters when you need multiple charts reporting on different dimensions. Without a component like this, each chart would require its own data-shaping utility. The initial setup of column IDs and dataset IDs is admittedly fiddly, but once the component is wired to the dataset, reconfiguring charts requires no rewriting of formatting functions.



