Scaffold a new app
npx create-definite-app my-app — scaffold + build with one commandFramework on npm
@definite-app/data-apps — the build tool, runtime, and component libraryWhen to use data apps vs YAML dashboards
| YAML Dashboard | Data App |
|---|---|
| Standard charts, tables, KPIs | Custom interactive UI (expandable rows, conditional formatting) |
| Multiple tiles in a grid layout | Single full-screen experience |
| Cube/SQL datasets with built-in viz | Custom JavaScript visualizations (ECharts, Perspective.js) |
| Quick to build with Fi | Source-authored React with component library |
| No build step required | Compiled to single HTML via build tool |
How data flows
- app.json declares every data resource the app needs. Each resource has a key, a
kind, and asource(SQL, Cube, or file). - The Definite platform reads the manifest and fetches data server-side from DuckLake, Cube, or GCS. The app never talks to the warehouse directly.
- The runtime loads the fetched data into a browser-side DuckDB WASM instance as local tables.
kind: "dataset"resources become DuckDB tables;kind: "json"resources are returned as plain arrays. - App.tsx queries those local tables via
useSqlQuery(dataset, sql, deps). These queries run in the browser against DuckDB WASM, not against the server.
Column names in your
useSqlQuery SQL must match the aliases in your app.json SQL. If app.json has SELECT foo AS myColumn, the local table column is myColumn. Mismatches cause “Binder Error: Referenced column not found” at runtime.Quick start
Scaffold a new data app from anapp.json manifest with one command:
create-definite-app on npm. It depends on @definite-app/data-apps, which ships the build script (definite-build), runtime, components, and templates.
This produces:
Step 1: Define your data in app.json
Declare the data resources your app needs:Step 2: Build your UI in App.tsx
Step 3: Build
my-app/dist/index.html (and dist/index.embedded.html for multi-tenant embedded use). The build validates that all useDataset() and useJsonResource() calls reference keys that exist in app.json.
Step 4: Deploy
Uploaddist/index.html to Definite Drive (via the MCP server or UI) and create a Doc with a full-screen HTML tile:
Manifest resources
Resource kinds
| Kind | Hook | Use for |
|---|---|---|
dataset | useDataset(key) | Data loaded into browser DuckDB WASM as a queryable table |
json | useJsonResource(key) | Small lookup lists returned as plain arrays (dropdowns, config) |
Source types
| Type | Description |
|---|---|
sql | SQL executed server-side against DuckLake. Recommended. |
duckdbFile | A .duckdb file downloaded from Drive/GCS and attached locally |
cube | Cube semantic model query. Not recommended for data apps. |
Public embeds
For publicly shared Docs, resources need asnapshot block:
Runtime hooks
The runtime library provides React hooks for data loading and querying:| Hook | Returns | Purpose |
|---|---|---|
useDataset(key) | DatasetHandle | Load dataset into browser DuckDB, get tableRef for SQL |
useSqlQuery(dataset, sql, deps) | QueryState<T> | Run client-side SQL against loaded dataset |
useJsonResource(key) | QueryState<T> | Load JSON resource as array |
useTheme() | { theme, toggleTheme } | Dark/light mode |
usePerspective(dataset) | { client, perspectiveTable } | Initialize Perspective viewer for a dataset |
useDataset, useJsonResource) cache results in IndexedDB with a 24-hour TTL. Call refresh() on the returned handle for a hard refresh.
UI components
The runtime includes a full component library. To see exported components, hooks, and types, grep the runtime source afternpm install:
| Category | Components |
|---|---|
| Layout | AppShell, Card, TabGroup |
| Data display | KpiCard, DataTable, ReportTable, Badge |
| Charts | EChart, PerspectivePanel |
| Inputs | Select, MultiSelect, FilterPills, TextInput, DateInput |
| Feedback | LoadingState, ErrorState, Tooltip, ResourceCacheBadge |
Best practices
- Always use SQL resources (
type: "sql") over Cube resources for data apps. You control column names via aliases. - Alias columns to camelCase in
app.jsonSQL. Convert dates withSTRFTIME(col, '%Y-%m-%d'). - Keep client-side SQL simple. Use
app.jsonSQL for joins, CASE WHEN, and complex filters. UseuseSqlQueryonly for GROUP BY, SUM of pre-computed columns, and date range filters. - Cast SUM results to
::INTEGERin client-side SQL. DuckDB WASM may return HUGEINT which JavaScript can’t handle cleanly. - Pre-compute conditional flags in
app.jsonSQL. DuckDB WASM has known issues with compoundCASE WHENexpressions (they silently return 0).
Caching
The runtime caches data loads in IndexedDB with a 24-hour TTL. Cache keys include the resource key, mode, and manifest definition, so rebuilt apps invalidate automatically. UseResourceCacheBadge in your AppShell meta slot to show cache status and provide a “Clear cache & reload” button.
Next steps
create-definite-app
The CLI scaffolder. Generates
App.tsx from a manifest in one shot.@definite-app/data-apps
The framework: build tool, runtime, components, examples.
Agent Reference
Programmatically create data apps via the MCP server or AI agents
Tile Types Reference
Configuration options for all tile types including HTML tiles

