Charts
Chart::make($title) renders a data chart. You feed it rows with ->data(), name the category (X)
axis with ->categoryKey(), then declare one or more series — each series plots one data key as a
line, bar, area, or pie. The PHP builder serializes to a typed node the renderer draws with
Recharts.
Chart::make('Signups') ->description('New users per month') ->categoryKey('month') ->data([ ['month' => 'Jan', 'free' => 240, 'pro' => 90], ['month' => 'Feb', 'free' => 300, 'pro' => 140], ['month' => 'Mar', 'free' => 280, 'pro' => 180], ['month' => 'Apr', 'free' => 360, 'pro' => 240], ['month' => 'May', 'free' => 420, 'pro' => 320], ]) ->line('free', 'Free') ->line('pro', 'Pro') ->height(260);- categoryFormat:null
- categoryKey:"month"
- data:[{"free":240,"month":"Jan","pro":90},{"free":300,"month":"Feb","pro":140},{"free":280,"month":"Mar","pro":180},{"free":360,"month":"Apr","pro":240},{"free":420,"month":"May","pro":320}]
- description:"New users per month"
- grid:true
- height:260
- legend:true
- title:"Signups"
- tooltip:true
- valueFormat:null
- xAxis:true
- yAxis:true
Data and series
Section titled “Data and series”A chart is ->data() — a list of rows — plus one or more series. Each series names the row key it
plots, with an optional display name and color:
->line($dataKey, $name?, $color?)— a line.->bar($dataKey, $name?, $color?, $stackId?)— a bar; stackable via$stackId.->area($dataKey, $name?, $color?, $stackId?)— a filled area; stackable via$stackId.->pie($dataKey, $nameKey?, $name?, $color?)— a pie;$nameKeynames each slice.
->categoryKey($key) names the row field used for the X axis. ->height($px) sets the plot height
(default 320).
Bar charts
Section titled “Bar charts”Two ->bar() series render grouped side by side:
Chart::make('Orders by channel') ->categoryKey('week') ->data([ ['week' => 'W1', 'online' => 120, 'store' => 80], ['week' => 'W2', 'online' => 150, 'store' => 70], ['week' => 'W3', 'online' => 170, 'store' => 90], ['week' => 'W4', 'online' => 210, 'store' => 110], ]) ->bar('online', 'Online') ->bar('store', 'In-store') ->height(260);- categoryFormat:null
- categoryKey:"week"
- data:[{"online":120,"store":80,"week":"W1"},{"online":150,"store":70,"week":"W2"},{"online":170,"store":90,"week":"W3"},{"online":210,"store":110,"week":"W4"}]
- description:null
- grid:true
- height:260
- legend:true
- title:"Orders by channel"
- tooltip:true
- valueFormat:null
- xAxis:true
- yAxis:true
Stacking
Section titled “Stacking”Give bars — or areas — the same stackId to stack them into one column instead:
Chart::make('Monthly recurring revenue') ->description('Stacked by revenue type') ->categoryKey('month') ->data([ ['month' => 'Jan', 'new' => 1200, 'expansion' => 300], ['month' => 'Feb', 'new' => 1500, 'expansion' => 450], ['month' => 'Mar', 'new' => 1800, 'expansion' => 600], ['month' => 'Apr', 'new' => 2100, 'expansion' => 780], ]) ->bar('new', 'New', stackId: 'mrr') ->bar('expansion', 'Expansion', stackId: 'mrr') ->height(260);- categoryFormat:null
- categoryKey:"month"
- data:[{"expansion":300,"month":"Jan","new":1200},{"expansion":450,"month":"Feb","new":1500},{"expansion":600,"month":"Mar","new":1800},{"expansion":780,"month":"Apr","new":2100}]
- description:"Stacked by revenue type"
- grid:true
- height:260
- legend:true
- title:"Monthly recurring revenue"
- tooltip:true
- valueFormat:null
- xAxis:true
- yAxis:true
Area and composed charts
Section titled “Area and composed charts”Series types mix freely in one chart. Declaring an ->area() and a ->line() together layers the
line over the filled band:
Chart::make('Revenue vs forecast') ->description('Actuals as a line over the forecast band') ->categoryKey('month') ->data([ ['month' => 'Jan', 'forecast' => 26000, 'revenue' => 28000], ['month' => 'Feb', 'forecast' => 30000, 'revenue' => 32000], ['month' => 'Mar', 'forecast' => 34000, 'revenue' => 36500], ['month' => 'Apr', 'forecast' => 37000, 'revenue' => 34000], ['month' => 'May', 'forecast' => 39500, 'revenue' => 41500], ]) ->area('forecast', 'Forecast') ->line('revenue', 'Revenue') ->height(260);- categoryFormat:null
- categoryKey:"month"
- data:[{"forecast":26000,"month":"Jan","revenue":28000},{"forecast":30000,"month":"Feb","revenue":32000},{"forecast":34000,"month":"Mar","revenue":36500},{"forecast":37000,"month":"Apr","revenue":34000},{"forecast":39500,"month":"May","revenue":41500}]
- description:"Actuals as a line over the forecast band"
- grid:true
- height:260
- legend:true
- title:"Revenue vs forecast"
- tooltip:true
- valueFormat:null
- xAxis:true
- yAxis:true
Pie charts
Section titled “Pie charts”A ->pie() series plots one value per row as slices. nameKey names each slice; give a row a color
field to color its slice, or let the theme palette pick:
Chart::make('Revenue by channel') ->description('Share of total revenue') ->data([ ['channel' => 'Direct', 'amount' => 42000, 'color' => '#2563eb'], ['channel' => 'Partner', 'amount' => 27000, 'color' => '#16a34a'], ['channel' => 'Marketplace', 'amount' => 19000, 'color' => '#f59e0b'], ['channel' => 'Retail', 'amount' => 12000, 'color' => '#dc2626'], ]) ->pie('amount', nameKey: 'channel') ->height(260);- categoryFormat:null
- categoryKey:null
- data:[{"amount":42000,"channel":"Direct","color":"#2563eb"},{"amount":27000,"channel":"Partner","color":"#16a34a"},{"amount":19000,"channel":"Marketplace","color":"#f59e0b"},{"amount":12000,"channel":"Retail","color":"#dc2626"}]
- description:"Share of total revenue"
- grid:true
- height:260
- legend:true
- title:"Revenue by channel"
- tooltip:true
- valueFormat:null
- xAxis:true
- yAxis:true
Colors and theming
Section titled “Colors and theming”Omit a series color and Lattice cycles a palette built on your theme tokens —
--lt-primary, --lt-success, --lt-info, --lt-warning, --lt-danger — so charts follow light and
dark mode automatically. Pass an explicit color to override: a hex value, or a CSS variable like
var(--lt-primary) to stay theme-aware. For pies, a per-row color field wins over the series color.
Formatting values and categories
Section titled “Formatting values and categories”By default the axes print raw values. Attach a format to render them locale- and timezone-aware —
the same Intl formatting the tables use. The value axis (always numeric) takes a NumberFormat; the
category axis takes either a NumberFormat or a DateFormat.
Chart::make('Revenue') ->description('Compact currency on the value axis, short dates on the category axis') ->categoryKey('month') ->data([ ['month' => '2026-01-01', 'revenue' => 28000], ['month' => '2026-02-01', 'revenue' => 32000], ['month' => '2026-03-01', 'revenue' => 36500], ['month' => '2026-04-01', 'revenue' => 41500], ]) ->line('revenue', 'Revenue') ->categoryFormat(DateFormat::monthYear()) // raw dates → 'Jan 2026', localized ->valueFormat(NumberFormat::currency('USD')->compact()) // 28000 → $28K ->height(260);- categoryFormat:{"dateStyle":null,"kind":"date","month":"short","timeStyle":null,"year":"numeric"}
- categoryKey:"month"
- data:[{"month":"2026-01-01","revenue":28000},{"month":"2026-02-01","revenue":32000},{"month":"2026-03-01","revenue":36500},{"month":"2026-04-01","revenue":41500}]
- description:"Compact currency on the value axis, month labels on the category axis"
- grid:true
- height:260
- legend:true
- title:"Revenue"
- tooltip:true
- valueFormat:{"currency":"USD","kind":"number","maximumFractionDigits":null,"minimumFractionDigits":null,"notation":"compact","unit":null}
- xAxis:true
- yAxis:true
NumberFormat mirrors the numeric table columns: ->decimals($min, $max?), ->compact(),
NumberFormat::currency($code), and ->unit(NumberFormatUnit::Percent). For dates,
DateFormat::date(), ::time(), and ::dateTime() take a DateTimeStyle (Full/Long/Medium/Short),
while DateFormat::month() and ::monthYear() render just the month (Jan) or month and year
(Jan 2026) — pass long: true for the full month name. All format with the active locale and
timezone, so you feed the chart raw dates instead of pre-translated labels.
Chrome
Section titled “Chrome”Every part of the chart frame toggles independently. All default to on:
->legend(bool)— the series legend.->tooltip(bool)— the hover tooltip.->grid(bool)— the background grid.->xAxis(bool)/->yAxis(bool)— the axes.->description($text)— a subtitle under the title.
Chart::make('Signups') ->data($rows) ->categoryKey('month') ->line('total') ->legend(false) ->grid(false);Dynamic series
Section titled “Dynamic series”When the series aren’t known ahead of time, build ChartSeries value
objects and hand them to ->series([...]) instead of the per-type helpers. The static factories
mirror the fluent methods:
use Lattice\Lattice\Core\Values\ChartSeries;
$series = collect($metrics) ->map(fn (string $key): ChartSeries => ChartSeries::line($key, ucfirst($key))) ->all();
Chart::make('Metrics') ->data($rows) ->categoryKey('day') ->series($series);