Skip to content
quartile
StarGet started
0.1 previewExplore data. Compare cohorts. Review a model.

Build data apps,
not just charts.

Turn datasets into analytical workspaces. Explore editable tables, compare distributions and keep every view linked. Quartile brings 71 core React components, including 13 chart types, onto one data model — with optional, reviewable AI assistance.

Get startedOpen Studio

Open-source preview. Start in Studio or follow the installation guide. Not published to npm yet.

Version
0.1.0 preview · Oct 2026
License
Apache-2.0
Runtime
React 18 · 19
Renderer
SVG · Canvas / WebGL scatter
Data in
JSON rows · optional Arrow queries
Core build
128 kB JS + 22 kB CSS gzipBefore tree-shaking; dependencies and worker assets excluded.

Loading the revenue workspace… Explore the storefront example

A live revenue workspace built with Quartile. Change the period, select a market, follow the data. Explore the full storefront example

Two ways in. Your data, your code.

Start with a question.
Build it your way.

Compose a workspace in React, or shape one visually in Studio. Start with a real API and keep the result in your own application.

Choose your starting path
  1. Add Quartile to your app

    Build and pack the preview, then import the components and stylesheet.

  2. Connect an API

    Use a documented fetch helper that returns typed rows, units and source metadata.

  3. Compose your workspace

    Bring charts, filters and tables together in React. Your application owns the data.

Read the installation guide
7 days · hourly · New York

Weather forecast

One row per forecast hour. Explore temperature, humidity, precipitation and wind with their units attached.

Use this API in React

Data from Open-Meteo. CC BY 4.0 data. The free endpoint is for non-commercial use.

The Studio requests data from the selected provider. You can inspect the source, retry a failed request and export the code. No account required.

One system

Charts are a third of a data app.

Most teams glue a chart library to a UI kit, then spend weeks reconciling themes, tooltips and filter state. Quartile ships all three layers as one system, on the same tokens and the same data contract.

Interfacecomponents
51

Forms, navigation, overlays and feedback for the rest of the app.

ButtonSelectComboboxDateRangePickerDialogTabsCommandMenuToast+43
Datacomponents
7

The analytical layer: editable explorers, metrics, filters, shared selection and the spec renderer.

KPIKPIGroupDataTableDataExplorerFilterBarSelectionSpecView
Chartscomponents
13

One set of scales, axes and tooltips, from sparklines to sankeys.

LineChartAreaChartBarChartScatterPlotHeatmapFunnelSankeyBoxPlot+5
One theme

Surfaces, series and states read the same CSS variables. Light and dark ship in one stylesheet; a brand theme is a remap of those variables.

One data model

Plain JSON rows in. Field types, labels and formats are inferred (or declared with dataset()) and shared by every chart, table and filter. An optional Arrow query source brings bounded worker results into the same model.

One selection

Brushes, clicks and filters publish to a shared store. Any component inside a <Selection> reads it; none of them need props for it.

For the work between question and answer

Get closer to your data.

Inspect a distribution. Find the missing observations. Compare a subgroup, correct a value, and keep the view you made. An analytical table belongs in the same conversation as your charts.

Open the full exploration workspace
materials.csv72 simulated observations · local memory
Observations in view
72/ 72
Mean yield
74.7%
Missing yield
4

Search and filters shape the plot. A brush narrows the table. Means exclude missing values.

Yield distribution

Brush to inspect a range

Distribution of Yield, 68 in 12 bins from 50.0% to 100.0%. Highest: 75.0% – 79.17% (8).

Experiment yield distribution
YieldCount
50.0% – 54.17%3
54.17% – 58.33%6
58.33% – 62.5%7
62.5% – 66.67%5
66.67% – 70.83%7
70.83% – 75.0%6
75.0% – 79.17%8
79.17% – 83.33%6
83.33% – 87.5%6
87.5% – 91.67%5
91.67% – 95.83%7
95.83% – 100.0%2
72 of 72 source rows
EXP-001AlloyB-01
EXP-002PolymerB-01
EXP-003CeramicB-01
EXP-004AlloyB-01
EXP-005PolymerB-01
1–5 of 72 rows · Select rows to publish to “landing-science”
Try a preset, brush the distribution, or edit a yield value.Scroll the table for more columns

Shape the view

Typed filters, search and sort priorities. Group records with explicit aggregates; pin, resize, reorder and hide columns.

Keep the reasoning

View state is serializable JSON. Save and restore a configuration, export all matching rows, and validate edits before your app accepts them.

Bring the next dataset

Pass typed JSON rows from pandas or Polars. The full workspace also imports CSV, JSON and dataframe snapshots in the browser.

Dataframe handoff guide

Fictional materials data, running entirely in this page. Edits last until reload; use the View controls to copy or load a view configuration. Explore the table API

Linked by default

Brush one view. Every view listens.

Wrap components in a Selection. Brushing, clicking and filtering publish to one shared state, with no prop drilling and no event buses. Try it below; the log on the right shows what was published.

Order explorer / Kestrel Goods
Sample data Sep 7 — Oct 6, 2026
Shared selection3,295 / 3,295 orders
All orders. Brush the timeline or choose a category.
Orders
3,295
100.0% of all
Revenue
$429.8K
gross
Avg. order
$130.45
per order
Refund rate
10.65%
351 refunds
Orders per day 30 daily binsDrag to select a range

Orders per day of Date, Sep 7 to Oct 6: total 3,295. Highest: Sep 22 (129).

Orders per day
DateOrders
Sep 794
Sep 8108
Sep 9103
Sep 1098
Sep 11106
Sep 1281
Sep 1388
Sep 14107
Sep 15111
Sep 16108
Sep 17104
Sep 18122
Sep 19103
Sep 20102
Sep 21116
Sep 22129
Sep 23128
Sep 24110
Sep 25111
Sep 26102
Sep 2797
Sep 28111
Sep 29119
Sep 30128
Oct 1113
Oct 2128
Oct 3108
Oct 4105
Oct 5127
Oct 6128
Revenue by categoryClick to filter

Amount by category, 5 items. Highest Apparel at $175,665.48; lowest Kids at $27,896.04.

Revenue by category
CategoryAmount
Apparel$175,665.48
Footwear$126,872.14
Home$68,646.86
Accessories$30,761.40
Kids$27,896.04
Largest orders in selection
KST-12980Sep 30Apparel$359.60Paid
KST-13645Oct 6Apparel$359.33Paid
KST-10697Sep 9Apparel$359.06Refunded
KST-12992Oct 1Apparel$358.94Paid
KST-13215Oct 2Apparel$358.91Refunded
ImplementationOrdersExplorer.tsx
import { BarList, DataTable, Histogram, KPI, Selection } from '@quartile/react';

const kpis = [
  { label: 'Orders', aggregate: 'count' },
  { label: 'Revenue', value: 'amount', format: 'currency-compact' },
  { label: 'Avg. order', value: 'amount', aggregate: 'mean' },
];
const columns = [
  { field: 'id', label: 'Order' },
  { field: 'date' },
  { field: 'category' },
  { field: 'amount', cell: 'number' },
];

export function OrdersExplorer({ orders, onChange }) {
  return (
    <Selection id="orders" onChange={onChange}>
      <KPI.Group data={orders} items={kpis} variant="strip" />
      <Histogram data={orders} x="date" brush />
      <BarList data={orders} category="category" value="amount" select />
      <DataTable data={orders} columns={columns} sort="-amount" limit={5} />
    </Selection>
  );
}
Scroll code ↔ ↕ · focus and use arrow keys
Selection eventsAwaiting input
  1. readyselection "orders" · 3,295 rowsBrush or click a view to inspect its predicate here.
An assistant with a review step

A proposal you can inspect.

Give an assistant the context you choose. Quartile validates its proposed filters, sorts and chart changes against your fields. You review the plan, then apply it to application-owned state.

Explore the assistant workspace
Local demo · no model request

Choose a proposal to review.

Two deterministic recipes exercise the real assistant API. The brief delay makes cancellation visible. Your current view stays in place until you apply.

The adapter proposes. You decide what changes.
Inspect the context available to this adapter

Field metadata, bounded profile summaries and current filters. No raw records. Inspect these values before sending context to your own backend.

{
  "version": 1,
  "source": {
    "id": "landing-materials",
    "version": "1",
    "label": "Simulated materials experiments"
  },
  "fields": [
    {
      "name": "id",
      "label": "Run",
      "type": "nominal",
      "format": "text"
    },
    {
      "name": "material",
      "label": "Material",
      "type": "nominal",
      "format": "text"
    },
    {
      "name": "batch",
      "label": "Batch",
      "type": "nominal",
      "format": "text"
    },
    {
      "name": "temperature",
      "label": "Temperature (°C)",
      "type": "quantitative",
      "format": "integer",
      "unit": "COUNT"
    },
    {
      "name": "yield",
      "label": "Yield",
      "type": "quantitative",
      "format": "percent"
    },
    {
      "name": "reviewed",
      "label": "Reviewed",
      "type": "boolean",
      "format": "text"
    }
  ],
  "selection": [],
  "profile": {
    "totalRows": 72,
    "scannedRows": 72,
    "complete": true,
    "fields": [
      {
        "field": "id",
        "type": "nominal",
        "valid": 72,
        "invalid": 0,
        "missing": 0,
        "distinct": 72
      },
      {
        "field": "material",
        "type": "nominal",
        "valid": 72,
        "invalid": 0,
        "missing": 0,
        "distinct": 3
      },
      {
        "field": "batch",
        "type": "nominal",
        "valid": 72,
        "invalid": 0,
        "missing": 0,
        "distinct": 6
      },
      {
        "field": "temperature",
        "type": "quantitative",
        "valid": 72,
        "invalid": 0,
        "missing": 0,
        "distinct": 72,
        "min": 180,
        "max": 300,
        "mean": 240.6805555555556
      },
      {
        "field": "yield",
        "type": "quantitative",
        "valid": 68,
        "invalid": 0,
        "missing": 4,
        "distinct": 43,
        "min": 0.53,
        "max": 0.96,
        "mean": 0.7467647058823532
      },
      {
        "field": "reviewed",
        "type": "boolean",
        "valid": 72,
        "invalid": 0,
        "missing": 0,
        "distinct": 2
      }
    ]
  },
  "provenance": [
    {
      "label": "Source",
      "detail": "72 deterministic, fictional observations generated in this page."
    }
  ]
}

Applied view

72 / 72 observations

All 72 observations. No proposal applied.

Distribution of Yield, 68 in 10 bins from 50.0% to 100.0%. Highest: 80.0% – 85.0% (9).

Applied analysis distribution
YieldCount
50.0% – 55.0%3
55.0% – 60.0%8
60.0% – 65.0%7
65.0% – 70.0%8
70.0% – 75.0%8
75.0% – 80.0%8
80.0% – 85.0%9
85.0% – 90.0%6
90.0% – 95.0%7
95.0% – 100.0%4
RunMaterialYield
EXP-001Alloy—
EXP-002Polymer66.0%
EXP-003Ceramic79.0%
EXP-004Alloy92.0%
EXP-005Polymer61.0%

Your model. Your server. Your decision.

The optional @quartile/react/ai entry supplies the context, validation and request lifecycle. The runnable Node backend example keeps credentials on the server and supports a configured model. This page uses only the local adapter above.

Try the foundations

Inspect the behavior behind the interface.

Time a selection on your device, navigate a chart with the keyboard, and edit a validated JSON spec. These demos run the same preview package you can build from source.

Open the worker query workbench
In-memory selectionsmeasuring…

250K rows, timed in your browser.

Loading…
Loading…
0 matching / 0 source rows

Pick a size, brush the histogram or click a region. The timer includes row generation on size changes and stops at the DOM commit, before paint. Rows become 40 SVG bins and four HTML region totals; this measures in-memory aggregation rather than raw-point rendering. Changing the source size retains your active selection; use Clear selection to reset it.

Explore Arrow queries and scatter renderers ↗
Accessibility

Charts you can tab through.

12 bars. Highest Jun 26 at $1.01M; lowest Dec 25 at $566.4K.

Kestrel revenue per month
MonthRevenue
Nov 25$573.7K
Dec 25$566.4K
Jan 26$605.0K
Feb 26$648.5K
Mar 26$799.3K
Apr 26$876.1K
May 26$953.0K
Jun 26$1.01M
Jul 26$993.8K
Aug 26$928.8K
Sep 26$852.6K
Oct 26$815.7K

Tab to the chart, then use ← → Home End. The dark panel mirrors the chart's own live region, word for word. Charts also carry an auto-written summary and a data-table fallback; switch to Table to see it.

AI-ready

Typed schemas, generated dashboards.

› spec.json · edit me
Weekly sign-ups by plan

Starter from Jul 6 to Sep 28: up 26.5%, from 34 to 43. Low 34 on Jul 6; high 48 on Sep 7. Team from Jul 6 to Sep 28: up 30.8%, from 26 to 34. Low 21 on Jul 20; high 34 on Sep 28. Enterprise from Jul 6 to Sep 28: up 162.5%, from 8 to 21. Low 8 on Jul 6; high 21 on Sep 21. Total from Jul 6 to Sep 28: up 44.1%, from 68 to 98. Low 68 on Jul 6; high 98 on Sep 28.

Weekly sign-ups by plan
WeekStarterTeamEnterpriseTotal
Jul 63426868
Jul 133824870
Jul 2040211071
Jul 2739211474
Aug 337231777
Aug 1036271780
Aug 1738311483
Aug 2442321286
Aug 3146301187
Sep 748281490
Sep 1447271892
Sep 2144292194
Sep 2843342198
Schema and local data references valid · 0 errors

This local demo registers signups: 39 fictional weekly plan totals, with week, plan and count fields. No model is called.

16 chart and data components are described by one JSON Schema. Hand it to a model, check what comes back with validateSpec, render it with SpecView.

Components

Everything around the chart.

Inputs, overlays, navigation and feedback, designed for dense screens. Same tokens, same keyboard model, same density scale.

Browse the component library →
Button · SegmentedControl · Switch · Checkbox · RangeSlider

One isolated sample: controls below filter the order summary and its CSV, without changing the other demos.

Order value$40 – $180
2,419 of 3,295 sample orders match amount and status filters

Amount by category, 5 items. Highest Footwear at $65.7K; lowest Kids at $25.3K.

Gross value of matching sample orders
CategoryAmount
Footwear$65.7K
Home$62.3K
Apparel$61.0K
Accessories$28.3K
Kids$25.3K
2,419 matching orders · gross order value, including any selected refunds · Sep 7–Oct 6, 2026
DateRangePicker · Calendar
September 2026
MTWTFSS
Combobox · multiple · BarList
North AmericaEurope

Amount by region, 2 items. Highest North America at $179.3K; lowest Europe at $118.2K.

Revenue for the picked regions
RegionAmount
North America$179.3K
Europe$118.2K
Revenue per region, Sep 7 – Oct 6, 2026
CommandMenu
Inspect linked orders
Explore editable data
Build charts in Studio
Export all sample orders CSV
Copy component section link
or press ⌘ K
Toast · Badge · Delta · Tooltip
Notification preview
This specimen shows a success message. It has not exported a report.
LiveStale · 2hQuery failedDraft
+12.4%−3.1%±0.0%+0.21 pt
Sep 14, 2026
Revenue $48,210
Build from a working example

From first look to model review.

Start with a scientific question and a working interface. These examples use the public library and deterministic sample data; the explorer also accepts your own local files.

Read the data-science guide
From exploration to implementation

A clear path into your codebase.

Start with one linked view. Keep your data fetching, application logic and deployment choices. Quartile gives the interface a shared vocabulary.

Start building
Can I start with a dataframe?

Yes. Export typed JSON rows from pandas or Polars, or use the snapshot helper and executable notebook in the repository. The explorer imports CSV, JSON and typed snapshots in your browser. This is a file handoff; it does not connect to or synchronize a running Python kernel.

Who owns edits and saved table views?

Your application does. DataExplorer exposes controlled view state for filters, sort priorities, grouping, column layout and pagination. Cell edits pass through validation and an onCellEdit callback; grouped values are read-only. Save the serializable view wherever you choose. It contains configuration, not a copy of the dataset.

Does the assistant call a model or change data automatically?

This landing demo uses two deterministic local recipes and makes no model request. The optional AI entry can call an adapter connected to your backend. It shares explicitly chosen metadata, summaries and active filters, validates a proposed plan, and waits for Apply. It changes application-owned analysis state; the example does not execute arbitrary code or edit source records.

How do I try Quartile today?

Open Studio to connect a public API and build a view visually, then download a React starter. For an existing app, clone the public repository and build and pack @quartile/react locally. The 0.1 preview is not published to npm, and APIs can still change.

Can I use my own API with Studio?

Yes, for a public JSON endpoint that allows browser requests. Studio maps a bounded array of flat records to charts and tables, then exports configuration and React code. For authenticated or private data, use your own backend and compose Quartile in your app. Studio does not host a backend or store API credentials.

Will it work with my existing React app?

Quartile targets React 18 and 19. Import its stylesheet, add the provider, and compose the parts you need. Use a client component boundary in Next.js; the framework guide also covers Vite.

Can it look like our product?

Charts, tables and controls read the same CSS custom properties. Start with the built-in light or dark theme, choose a density, then remap the colors, spacing and typography. No Tailwind or CSS-in-JS runtime is required.

Which renderer should I use?

Scatter plots default to SVG and also offer Canvas and WebGL, while keeping keyboard navigation, point selection and a paged table view. WebGL falls back to Canvas and then SVG when needed. Compare the active renderer on your own device; a different rendering path is not a speed guarantee.

Can I query larger datasets in a worker?

The optional Arrow and DuckDB adapter applies selections before aggregation and returns bounded results. The worker example loads up to one million generated source rows, pages raw records and plots at most 10,000 points. It reports source rows, returned rows, active renderer and timings separately; those sample sizes are not a universal capacity limit.

What should I check before shipping?

The optional worker query path needs separately installed Arrow and DuckDB peers and hosted assets. Profile your own data and devices. Keyboard navigation, summaries and table views are implemented; screen-reader validation and a full accessibility audit remain open work.

Open source

Free, Apache-2.0 licensed, built in public.

The code, the issues and the plans live on GitHub. 0.1 is a preview: APIs can still change before the first npm release, and bug reports shape what comes next.

Core components
71
Chart types
13
Unit tests, passing
467
License
Apache-2.0

Counted from the 0.1.0 source on 2026-10-10: core-entry component exports and the Vitest suite. Optional query and AI helpers are outside this component count.