Lattice Grid Buy a licence

the Lattice Designer

Embeddable Dashboard Designer in JavaScript

Stop turning every new chart into a developer ticket. The Lattice Designer puts a dashboard builder inside your own application: your users drag charts, grids, KPI tiles and maps onto a page over the data you allow, switch to view mode to share it, and you keep the saved dashboard as plain JSON in your own storage.

Read the guide Get the demo source Lattice Designer vs AG Studio

This is the real thing, running over a small retail dataset: orders linked to customers. Add a widget, drag one into place, then switch to view mode.

Loading the designer

for the person building the report

Build a report in minutes, without waiting a sprint.

A report author sees only the data and the widgets you have chosen, and works on the page itself, so what they build is what their readers get.

A live canvas

Drag a widget to move it, pull a corner to resize it, press Delete to remove it. The canvas is the dashboard itself, drawn exactly as view mode draws it, so there is no preview to trust.

Start from the data

A data panel lists every source and field with its type, and shows how the sources relate. Drop a field onto the page and the designer adds a sensible widget: a KPI tile for a number, a grid for text.

Settings that match the widget

Select a widget and its properties appear: which fields feed it, how a measure is added up, the chart type, number and date formats, titles and legends. Switch a chart type and the fields it still uses are kept.

Undo anything

Every change is one step you can undo and redo, from adding a widget to editing a setting. A long drag or a slider scrub counts as one step, so undo goes back to where you were.

Several pages in one report

Keep a report on more than one page and move between them with page tabs. Each page has its own layout, and the whole report saves together.

Edit mode and view mode

Authors edit; readers view. Switching to view mode hides every editing control and leaves the dashboard, still live, still sortable and filterable.

for the developer who ships it

You decide what an author can touch.

The designer carries no storage, no keys and no endpoint of its own. You hand it your data and your rules, and it hands your dashboard back as JSON.

Sources and relationships

Name the sources an author may use and declare how they relate, such as orders to customers. Fields from related sources can then sit in the same widget, and the data panel only offers combinations that make sense.

Guardrails

Allow-list the sources, fields, widget kinds, chart types, formats and pages an author can use, and cap the widgets on a page. What you restrict is hidden rather than greyed out, and a saved dashboard that already uses it still opens, read-only and marked as locked.

Your storage

getState() and setState() move the whole report as plain JSON, and an event fires on every change. Put it in a database, a file or local storage. Saved dashboards keep loading as the designer grows, and a dashboard saved by a newer version is refused by name rather than half-loaded.

AI with your own model

Authors describe a page in words and the assistant proposes one as a highlighted preview they accept or reject, as one undo step. It reaches your model through your own callback, so there is no key or endpoint in the designer, and no row leaves the browser unless you turn on a bounded sample. Anything the model suggests that your guardrails refuse is dropped and named.

Config through the data router

Route dashboard config to the designer the way you route rows to a grid: it applies the changes widget by widget, so one widget updating does not redraw the others, and an author's own edits leave as config rows you can write back.

Looks and accessibility

The designer takes your theme, including the four design-system presets, in light, dark and high contrast. Every action works from the keyboard, widgets move and resize with the arrow keys, changes are announced to screen readers, and text and focus contrast meet WCAG 2.1 AA.

derived grids and statistics

Answers an analyst would otherwise ask for.

Authors can ask the next question of the data without a developer. A derived grid is built from another grid with a short list of steps, and it follows its parent live.

Derived grids

Filter, group and roll up, pivot, join a related source, sort and keep the top few. Build "top customers" from the orders grid by joining the customer, adding up each one's orders and keeping the best eight. The result is both a widget and a source other widgets can read, every step can be undone, and the data panel shows where it came from.

Statistics widgets

Add a column profile, a distribution, a correlation, a group comparison, a forecast or a model error widget from the same properties panel as any other chart. Each one is built on the grid's own statistics, so the figures match what the grid reports.

millions of rows

The same designer over data that never reaches the browser.

Point a source at DuckDB, ClickHouse or another pushdown engine and filters, groups, pivots and top-N steps run where the data lives. A derived grid reports which steps the engine answered and which engine ran them, and a step an engine cannot do is refused by name instead of being quietly computed on a partial page of rows.

How to connect your data →

five minutes to embed

Hand it your data and a place to save.

The designer is a separate, opt-in module that works in plain JavaScript, with no framework required. Give it an element, your sources and the viewers your page already uses, then store what it reports.

Follow the guide →

import { createGrid, createHeadlessGrid } from '@toclocoinc/lattice-grid';
import { createLayout } from '@toclocoinc/lattice-grid/modules/layout';
import { createChart } from '@toclocoinc/lattice-grid/modules/charts';
import { createDesigner } from '@toclocoinc/lattice-grid/modules/designer';

const designer = createDesigner(document.querySelector('#designer'), {
  mode: 'edit',
  sources: {
    orders:    { kind: 'rows', rows: orders,    rowKey: 'id' },
    customers: { kind: 'rows', rows: customers, rowKey: 'id' },
  },
  relationships: [{ from: 'orders.customerId', to: 'customers.id', type: 'many-to-one' }],
  guardrails: { maxWidgetsPerPage: 12 },
  state: await loadSavedDashboard(),               // or leave it out: one empty page
  factories: { createGrid, createHeadlessGrid, createLayout, createChart },
});

designer.on('state', ({ state }) => saveDashboard(state));   // your storage, your rules

See it, then build on it.

The demo is a complete plain-JavaScript page over orders, products and customers, with a starter dashboard, view mode, and save and load. Read it start to finish.