Technical Documentation: evidence-dev/evidence
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
How It Works
Evidence is an open-source, code-based alternative to drag-and-drop business intelligence tools.
- Create reports in code using SQL and markdown
- Develop using Evidence's Agent, or on your local machine with Claude Code / Cursor etc
- Self-host or publish on Evidence Studio
Install
macOS / Linux
curl -fsSL https://evidence.studio/install.sh | sh
evidence helpWindows
irm https://evidence.studio/install.ps1 | iex
evidence helpCreate a Project
evidence init my-project
cd my-project
evidence devPublish
Publish your project on Evidence Studio for hosted reports, or self-host the generated static site on your own infrastructure. See the publishing docs for options.
Join the Evidence Community
Join our Slack channel to get involved in the community's discussion, share what you're working on, or ask for help.
Contributing
Contributions are welcome — see CONTRIBUTING.md for how pull requests are reviewed and merged.
License
Evidence is licensed under the MIT license. See LICENSE for details.
Overview (`cli/README.md`)
EVD
Evidence Studio local development CLI.
Quick Start
# Install dependencies (from monorepo root)
pnpm install
# Run a CLI command from source — instant, no build (defaults to cli/test-environment)
pnpm evd query "select 1"
pnpm evd help
# Run the dev server from source against the test playground
pnpm evd dev
# Build the CLI binary (requires Bun installed globally)
pnpm cli:build
# Run the binary
./cli/dist/evidence
./cli/dist/evidence --help
./cli/dist/evidence dev --port 8080Architecture
/* Detailed source-code truncated for AI context efficiency. */Project Structure
.
├── adapter/
│ └── index.js # Custom SvelteKit adapter
│ # - Discovers & embeds static assets
│ # - Generates manifest
│ # - Compiles binary with Bun
│
├── cli/
│ ├── index.ts # CLI entry point
│ ├── args.ts # Argument parsing, help, banner
│ └── server.ts # Bun HTTP server + SvelteKit integration
│
├── src/
│ ├── routes/ # SvelteKit routes (the actual app)
│ ├── lib/ # Shared components
│ └── app.html # HTML template
│
├── static/ # Static assets (favicon, etc.)
├── dist/ # Build output (gitignored)
│ └── evd # The compiled binary
│
├── svelte.config.js # SvelteKit config (uses ./adapter)
├── vite.config.ts # Vite config
└── package.jsonHow It Works
1. Asset Embedding
The magic that makes a single binary possible. During build:
// adapter/index.js generates imports like:
import favicon_SVG_a1b2 from '../client/favicon.svg' with { type: 'file' };
export const assetMap = new Map([
['/favicon.svg', favicon_SVG_a1b2]
// ... all static files
]);Bun's { type: "file" } import attribute embeds the file contents into the binary.
2. The Adapter
adapter/index.js is a custom SvelteKit adapter that:
- Writes SvelteKit output - client assets, prerendered HTML, server code
- Generates asset imports - creates
assets.generated.tswith embedded file references - Generates manifest - SvelteKit's routing manifest
- Compiles - runs
bun build --compileto create the binary
3. The CLI
cli/ contains the runtime code that gets compiled:
args.ts- Parses--port,--help,--version, etc.server.ts- Starts Bun.serve(), handles static assets & SSRindex.ts- Wires it together, handles commands
4. Request Flow
Request → Bun.serve()
│
├─→ Static asset? → Serve from embedded assetMap
│
├─→ Prerendered route? → Serve embedded HTML
│
└─→ Otherwise → SvelteKit SSRDevelopment
Prerequisites
- Bun v1.2.18+ (required for running CLI source and for binary compilation)
- pnpm (for package management)
Day-to-day workflow
| What you're working on | Run | Build needed? |
|---|---|---|
evidence dev flow / Svelte routes |
pnpm evd dev |
no |
| Other CLI commands | pnpm evd <cmd> |
no |
| Final binary smoke test | pnpm cli:build |
yes |
Both scripts default to the cli/test-environment playground regardless of where you invoke them from. Pass --project to override the target directory.
Quick Install: Add `evidence` to Your PATH
After building the CLI binary, you can add it to your PATH for convenient global access:
# Symlink the binary to a directory in your PATH (requires sudo)
sudo ln -sf "$(pwd)/cli/dist/evidence" /usr/local/bin/evidence
# Verify it works
which evidence
evidence --versionBenefits of this approach:
- Always reflects the latest
dist/evidenceafter rebuilds (no re-install needed) - Works everywhere (scripts, subshells, GUI tools)
- Easy to revert (see below)
Undo the symlink:
# Remove the symlink
sudo rm /usr/local/bin/evidence
# If you want to restore the previous binary (backup created during install)
sudo mv /usr/local/bin/evidence.old /usr/local/bin/evidenceNote: If you built the CLI elsewhere, replace the path accordingly. You can also prepend a user bin directory to your PATH instead of using /usr/local/bin if you prefer not to use sudo.
Examples — all run from the monorepo root
pnpm evd help
pnpm evd query "select 1" # against cli/test-environment (NDJSON)
pnpm evd --project ./some/dir validate # override to an arbitrary project
pnpm evd dev --port 5173 # extra flags forward to the dev serverOutput formats
Data commands are machine-readable by default; the human view is opt-in via--verbose. Output is identical whether or not stdout is a terminal (there is
no TTY detection). All formatting is centralized in cli/output.ts
(printResult / renderResult / fail) — commands never call JSON.stringify
or render tables directly.
Defaults (no flags):
- Result sets (
query,describe,schema) → NDJSON, one JSON object per line. - Name lists (
tables) → plainschema.table, one per line. - Structured/list results (
connectors,models,lineage,docs) → compact JSON. - No ANSI color and no box-drawing characters, ever, by default.
Shared flags on every data command:
| Flag | Effect |
|---|---|
--verbose |
Human view: pretty aligned table + full/extra columns |
--format <fmt> |
json, ndjson, csv, table (table = pretty). Wins over --verbose. |
--columns <a,b,c> |
Select and order columns |
--limit <n>, -l |
Limit rows (query defaults to 1000; a stderr note flags truncation) |
--all |
Remove the row limit |
--no-color |
Disable color (the NO_COLOR env var is also honored) |
Errors go to stderr; under --format json/ndjson they are emitted as{"error":<msg>,"code":<str>}. Status/progress chatter also goes to stderr.
Breaking changes (vs. the previous human-first defaults):
- Default output is now NDJSON/plain/compact-JSON instead of pretty tables.
Use--verbosefor the old human view.-vnow means--verbose. Use--version(or theversionsubcommand)
for the version.--columnsnow takes a value (column selection) instead of being a boolean
"columns-only" toggle — that role is nowdescribe's default.--formatis long-form only;-o/--outputkeeps its "write to
file" meaning.--jsonstill works as an alias for--format json.queryno longer pretty-prints by default and caps output at 1000 rows
unless--allis passed.
evd dev detects that it's running from source (via process.execPath) and routes through vite dev automatically — same auth + Studio health check as the binary, then spawns vite dev. Browser auto-open defaults to off in source mode (pass --open to opt back in); the binary still auto-opens by default.
What works under `evd` (no build)
The commands that are pure TypeScript and don't need the SvelteKit bundle: help, version, login, logout, whoami, orgs, switch, token, query, tables, describe, schema, connectors, models, lineage, upgrade.
What still needs `cli:build` (or the dev server)
validate and docs go through SvelteKit's HTTP layer, which uses import.meta.glob — a vite-only construct. Two options:
- Run
pnpm cli:buildonce, then./cli/dist/evidence validatereflects the latest source. (You only need to rebuild when SvelteKit-side code changes.) - Or run
pnpm evd devand hit the same endpoints directly:curl http://localhost:3000/api/validate.
Other commands
pnpm cli:build # Build production binary
pnpm cli:check # Type check
pnpm cli:lint # Lint & format check
pnpm --filter @evidence/cli formatDev vs Production
| Aspect | pnpm evd dev |
./dist/evidence |
|---|---|---|
| Server | Vite dev server (spawned) | Bun.serve() |
| Assets | Served from disk | Embedded in binary |
| HMR | Yes | No |
| Auto-open | No (pass --open) |
Yes |
| SSR | Yes | Yes |
| Size | N/A | ~50MB single file |
Adding CLI Commands
Edit cli/args.ts to add new commands:
// In parseArgs():
if (firstArg === 'mycommand') command = 'mycommand';
// In cli/index.ts:
case 'mycommand':
// do something
break;Data-producing commands should route their results through printResult (fromcli/output.ts) using the shared args.output options, rather than callingJSON.stringify or rendering tables themselves — this keeps the machine-first
defaults, --format, --columns, --limit, and color handling consistent
across every command.
Modifying the Adapter
The adapter in adapter/index.js controls the build process. Key functions:
discoverAssets()- Finds all files to embedgenerateAssetImports()- Creates the import statementsadapt()- Main build orchestration
Docker Image
The CLI is published as a Docker image, evidencedev/serve (Docker Hub), so that self-hosting a project is a two-line Dockerfile:
FROM evidencedev/serve:latest
COPY --chown=evidence:evidence . /projectThat's the whole point of the image — a user self-hosting a project should never have to figure out how to install the CLI inside a container. cli/Dockerfile handles fetching the right binary for the architecture, CA certificates (without them, TLS to the warehouse fails at runtime), a non-root evidence user, /project as the working directory, honouring a platform-provided $PORT, and starting evidence serve --host 0.0.0.0. The user's layer is just "copy my project in".
It ships on a version bump
The image does not build the CLI. Its first stage downloads an already-published binary from Vercel Blob:
https://.../cli/v${EVIDENCE_VERSION}/evidence-${target}So a new image only appears when a new CLI version is released. In .github/workflows/cli-release.yml, the docker job has needs: release — it cannot run until the release job has uploaded binaries for that version. There is no way to rebuild the image against unreleased CLI code: bump VERSION in cli/cli/args.ts, add the cli-release label to the PR, and merge.
Also bump the ARG EVIDENCE_VERSION default in cli/Dockerfile to match. CI overrides it, but anyone running docker build -f cli/Dockerfile . by hand gets whatever the default says.
Tags and platforms
evidencedev/serve:<version>on every release.evidencedev/serve:latestonly for stable versions — a version containing-(0.9.2-alpha.1) never moveslatest.- Built for
linux/amd64andlinux/arm64via QEMU + Buildx. Pushed with theDOCKERHUB_USERNAME/DOCKERHUB_TOKENsecrets.
The docker job lives in the release workflow rather than its own file because the cli-v* tag is pushed with GITHUB_TOKEN, which cannot trigger another workflow.
Telemetry
The CLI reports anonymous usage — to opt out run evidence telemetry disable, or set EVIDENCE_TELEMETRY_DISABLED=1 or DO_NOT_TRACK=1. The full payload is documented at https://docs.evidence.studio/cli#telemetry.
Events go to POST /api/cli/event in studio (studio/src/routes/api/cli/event/), which allow-lists fields and forwards to PostHog. Three rules keep the "unique users" number honest: serve runs and the daily serve_heartbeat never create a PostHog person (count deployments by project_id instead), a machine id that cannot be persisted is sent as null rather than a fresh UUID per run, and the first run inside an agent sandbox or container is flagged sandbox and recorded personless. Authenticated CLI usage of Studio is captured server-side as cli_studio_request in authenticateCliRequest, keyed to the user's email and organization group, so it is not subject to client opt-out.
License
MIT
Overview (`docs/README.md`)
Evidence Docs
Autogenerated Components and Data Sources Docs
The following directories are autogenerated by scripts in the scripts/docs:
components- All user componentsdata-sources- All data sources
Do not edit these files manually! Instead run the docs generation scripts:
npm run docs:generate-components
npm run docs:generate-connectorsDevelopment
Install the Mintlify CLI to preview the documentation changes locally. To install, use the following command
npm i mintRun the following command at the root of your documentation (where docs.json is)
mint devOverview (`cli/test-environment/README.md`)
CLI dev playground
This directory is the local Evidence project the CLI is exercised against during development. Drop a connection.yaml here (gitignored) pointing at your dev warehouse and the scripts below will pick it up.
Setup
Create cli/test-environment/connection.yaml. Snowflake + dbt source files for this directory will be added in follow-up work — for now a minimal config is enough to exercise CLI commands.
Run from the monorepo root
# A CLI command, against this directory (the default)
pnpm evd help
pnpm evd query --sql "select 1"
# Dev server, against this directory (vite dev under the hood, no binary build)
pnpm evd devvalidate and docs go through the SvelteKit HTTP layer (which uses vite-only constructs), so they need either a binary build (pnpm cli:build) or a running dev server. See the main cli/README.md for the full breakdown.
These scripts work from anywhere inside the monorepo — they don't cd into this directory, they pass --project cli/test-environment to the CLI so process.cwd() semantics still resolve correctly. To target a different project, append --project ./other/path to either script.
Behind the scenes
--projectchdirs the CLI process and setsEVIDENCE_PROJECT_CWDso the SvelteKit dev server (which runs withcwd= the cli package) can find your project files viagetProjectCwd().evd devdetects "running from source" viaprocess.execPathand routes throughvite devinstead of the compiled Bun server. Browser auto-open is off by default in source mode; pass--opento opt in.
Overview (`docs/components/README.md`)
Component Docs
Autogenerated Components Docs
These docs are autogenerated by the scripts/generate-components-docs.ts script. Do not edit these files manually!
To regenerate the docs, run the following command:
npm run docs:generate-componentsOverview (`docs/data-sources/README.md`)
Data Source Docs
Autogenerated Data Source Docs
These docs are autogenerated by the scripts/generate-connector-options.ts script. Do not edit these files manually!
To regenerate the docs, run the following command:
npm run docs:generate-connectorsOverview (`core/src/user-components/UserComponentModel/README.md`)
UserComponentModel
UserComponentModel is the foundation for all of our user-facing data components (tables, dimensions, measures, pivots, etc).
Think of it like the blueprint for every component: it defines the rules for how components talk to each other, what data they hold, and how they can be serialized/deserialized during SSR.
Why is it abstract?
An abstract class is like a "template" that you can't use directly.
You don't create a UserComponentModel on its own—you only create subclasses like TableModel or DimensionModel that fill in the blanks.
This lets us:
- Define shared rules once (e.g. parent/child validation, attribute reactivity, serialization).
- Ensure every subclass plays by the same rules, while still giving them the freedom to customize.
Analogy:
- Abstract class = "You must have wheels, but you can decide if you're a car, a bike, or a skateboard."
Attributes
Every UserComponentModel accepts a MaybeGetter for the component's attributes in its constructor argument. MaybeGetter is used to allow the attributes to be either a plain value (Record<string, unknown>) or a function that returns a value (a getter: () => Record<string, unknown>). Accepting a function is necessary for reactivity when passing state around. Reactivity isn't always necessary (like on the server during SSR)
so its a MaybeGetter.
In the constructor, the attributes use something called useStable. This prevents reactivity churn when the user is typing in the editor. When the user types, a new Markdoc RenderableTree is generated with every keystroke (after a debounce). This means that every model gets a brand new attributes object each time, which would cause all of the queries to re-run. To prevent this, useStable only returns a new value if the state has actually changed by doing a recursive comparison of values all the way down objects and arrays.
Parents and Children
Parent
- The "container" this model lives inside.
- Example: A
DimensionModelcan only exist inside aTableModel.
Children
- Models that live inside this one.
- Example: A
TableModelcan contain multipleDimensionModelandMeasureModelchildren.
This creates a tree structure with our models that mimics the RenderableTree from markdoc. It enables parents and children to depend on and interact with each other to run queries.
Why we check parent/child at runtime
Even though TypeScript checks parent/child relationships at compile time, we also validate them at runtime (isValidParent and isValidChild). This is necessary because the user's input is completely dynamic and they could build components with invalid parent/child relationships, which would result in us creating models with invalid parent/child relationships. By checking the types of parents/children, we make sure we fail fast with a clear error rather than something failing obscurely down the line.
const dimension = new DimensionModel(...);
table.addChild(dimension); // ✅ Works
const measure = new MeasureModel(...);
dimension.addChild(measure); // ❌ Throws runtime errorSerialization and SSR
Serialization and Deserialization is the process of converting a complex object into a JSON representation and then back again. This process is necessary because only JSON can be sent from the server to the client, not classes, functions, or other complex entities.
If a component needs to be serializable, it should specify its serialized type via the Serialized generic, implement the toSerialized function to return its serialized data, and perform necessary deserialization using the serialized data in the model constructor.
Note: We don't have to serialize all the way down to JSON! SvelteKit uses devalue under the hood which supports some more complex types like Date and Map.
Generics
What even are generics?
Generics are a way of writing code that is flexible but still type-safe.
Think of them as placeholders for types. Instead of hard-coding "this must always be a string" or "this must always be a number," we say "this can work with any type, but once you pick the type, it stays consistent."
For example, imagine we need a function that ensures something is always an array. We can pass it an argument that may or may not be an array, and it will ensure that what is returned is an array. This kind of function doesn't care if we're dealing with an array of strings, numbers, or anything else. We can use generics to make the types behave nicely with any kind of input.
function makeArray<T>(input: T | T[]): T[] {
return Array.isArray(input) ? input : [input];
}
const example1 = makeArray(1); // -> number[]
const example2 = makeArray(['a', 'b', 'c']); // -> string[]
const example3 = makeArray(new SomeClass()); // -> SomeClass[]UserComponentModel's generic types work fundamentally the same. They ensure consistency between the types of attributes, parents, children, and the serialized version of a model.
Why do they look weird/extra complex for UserComponentModel?
You'll notice the type definition uses a big object of options rather than simple type parameters:
type TableModelGenerics = WithDefaults<{
Attributes: TableAttributes;
Serialized: SerializedTableModel;
ValidChildren: [typeof DimensionModel, typeof MeasureModel, typeof PivotModel];
}>;Why?
- Clarity - Instead of positional generics (
<A, S, P, C>), we structure them in an object where each type has an explicit name from its key. This is more readable when there are a lot of generic types. - Flexibility - You don't always need to set all options. Using the
WithDefaultshelper, you can override just the parts you need, and leave the rest at defaults.
State in models
For reactivity, use $state and $derived (or $derived.by) for models' member variables.
Use $state for data that is changed during reactivity (e.g. page, search, order in TableModel)
Use $derived (or $derived.by) for state that is computed (derived) from other state like $state declarations, the model's attributes, etc (e.g. queryConfig in TableModel)
Note: Use $derived or derived.by for computed state, NOT getters (get myField() { }). Getters will be re-executed whenever they are read resulting in performance issues.
Overview (`core/src/user-components/common/README.md`)
Common Utilities
This directory contains shared utility functions used across user components.
Date System Architecture
The Problem We Solved
When building a data visualization platform, dates are everywhere but behave differently depending on context. Users want to:
- Filter data by date ranges ("Show me last 30 days")
- Compare time periods ("How does this month compare to last month?")
- Group data by time periods ("Monthly sales trends")
The challenge: date logic was scattered across components with inconsistent behavior. Some components parsed "Last 30 days" differently, comparison tooltips showed placeholder dates, and we had duplicate parsing logic everywhere.
Our Solution: Three Clear Use Cases
We identified that all date operations fall into three distinct patterns, each requiring different technical approaches:
1. Explicit Date Filtering ("Show me sales from last month")
The Use Case: User wants to see data from a specific time period. They select "Last 30 days" from a dropdown, and we filter the entire dataset.
Why This Exists: Most basic date filtering - users want to slice data by time ranges. Common in dashboards where you want to focus on recent data.
How It Works: We convert "Last 30 days" into SQL like WHERE date >= '2024-01-01' AND date <= '2024-01-31'
For Comparisons: We shift the date boundaries - if current period is "last month", comparison period becomes "two months ago"
2. Time-Series Analysis ("Monthly sales trends with comparisons")
The Use Case: User wants to see data grouped by time periods (daily, monthly, yearly) and compare each period to its predecessor. A table showing "Monthly Revenue" where each row is a month, and they want to see how each month compares to the previous month.
Why This Exists: Time-series analysis is core to business intelligence. Users don't just want to see monthly totals - they want to see "Is this month better than last month?" for every month.
How It Works: Instead of filtering dates, we group by time periods using date_grain="month". For comparisons, we shift the date expressions themselves: toStartOfMonth(date - INTERVAL 1 MONTH).
The Tricky Part: Each row represents a different time period, so the comparison logic needs to be applied per-row, not per-query.
3. Dashboard-Wide Date Controls ("Apply this date range to everything")
The Use Case: User sets a date range at the dashboard level and wants it applied to all components. Common in executive dashboards where you want to see "Q3 performance" across all metrics.
Why This Exists: Consistency - users want to change one date filter and have it affect multiple charts/tables simultaneously.
How It Works: Similar to explicit filtering but propagated across multiple components. The date range logic is reused but applied at a higher level.
Key Technical Decisions
MIN/MAX Aggregates for GROUPING SETS
Problem: Comparison tooltips showed "Jan 1/70" (ClickHouse null date placeholders) for total rows.
Solution: Wrap date expressions in MIN/MAX aggregates:
-- Before
formatDateTime(toStartOfMonth(date), '%b %e/%y')
-- After
formatDateTime(MIN(toStartOfMonth(date)), '%b %e/%y')Why: GROUPING SETS automatically calculates correct date ranges for both individual rows and totals without manual conditional logic.
Centralized Date Parsing
Problem: Duplicate parsing logic across multiple files.
Solution: Single parseDateRange() function in date-options.ts handles all patterns:
- "Last X days/months/years"
- "MTD/WTD/YTD"
- Case insensitive matching
- Singular/plural forms
Structured Return Values
Problem: Functions returned SQL strings that were then parsed with regex.
Solution: Return structured objects:
{ whereClause: string; startDate: string } | undefinedFunctions Overview
`processDateRange(date_range)`
- Converts date range objects to SQL WHERE clauses
- Returns both SQL and parsed start date
- Handles all supported date range patterns
- Uses date column from the DateRangeObject
`calculateComparisonDateRange(dateRange, offset)`
- Calculates comparison date ranges
- Handles both range-based and dimension-based shifting
- Uses centralized parsing logic
`parseDateRange(dateRange)`
- Central parsing for all date range strings
- Returns structured
ParsedDateRangeobject - Includes ClickHouse-specific fields
Common Patterns
Date Range Patterns
"Last 30 days"→ 30 days ago to today"Last month"→ Previous calendar month"MTD"→ Month to date"YTD"→ Year to date
Comparison Calculations
- Range-based: Shift boundaries by offset periods
- Dimension-based: Shift expressions by adding/subtracting intervals
When to Use What
Use Normal Range when:
- Adding explicit date filters to components (
date_range="Last 30 days") - User selects a date range from a dropdown
- You want to filter the entire dataset
Use Dynamic Range when:
- Component has
date_grainand you want period-over-period comparisons - Data is already grouped by time periods
- Each row represents a different time period
Use Table Range when:
- Applying dashboard-wide date filters
- Multiple components need the same date range
Common Gotchas
- "Jan 1/70" in tooltips → Use MIN/MAX aggregates around date expressions
- Comparison dates off by one → Check if you need the +1 day adjustment in
processDateRange - Case sensitivity → All parsing is case-insensitive, but double-check your strings
- MTD/YTD not working → Ensure you're using the
startDateFunctionfromparseDateRange
Migration Notes
- Eliminated
generateDateSqlwrapper function - Consolidated
calculatedateDimensionComparisonDateRangeinto main function - Removed string parsing in favor of structured returns
- No backwards compatibility maintained (as requested)
Other Utilities
comparisons.ts- Comparison query generationsql-options.ts- SQL expression utilitiesformat-options.ts- Value formatting helpers
Index (`docs/index.mdx`)
title: What is Evidence?
keywords:
- business intelligence
- BI tool
- business intelligence and analytics (ABI)
- data products
- dashboards
- reporting platform
- analytics platform
- data visualization
- business intelligence tools
- SQL reports
Evidence is a business intelligence and analytics (ABI) platform for building data products with SQL, markdown, and AI. Use it as a BI tool to write SQL reports, build dashboards, and publish an analytics platform your team can explore. It's a reporting platform for data visualization — one of the business intelligence tools your whole organization can use. Evidence comes in two parts: an open-source framework you can run yourself, and a hosted platform built on top of it.
Evidence Core
Evidence Core is an MIT-licensed framework for building data apps from SQL and Markdown. You write reports in Markdown with embedded SQL and components, query your own warehouse through direct connectors, and preview locally with the CLI. It builds to a static site you can self-host anywhere — Vercel, Netlify, Docker, or your own infrastructure. No account required.
Evidence Studio
Evidence Studio is the hosted platform built on Evidence Core. Alongside everything in Core, it adds:
- A collaborative editor and publishing with preview deployments
- The Evidence Agent for building reports with AI
- Access rules and page-level access control for controlling who sees what
- Scheduled reports and embedded analytics
Studio-only pages in these docs are marked with a badge.
Getting Started
Create your first report in minutes.
Learn More
Build reports to share with your team.
Quickstart (`docs/quickstart.mdx`)
title: 'Quickstart'
keywords:
- getting started
- build a dashboard
- business intelligence example
- business report example
- data analytics tutorial
Create an Account
This quickstart is the getting started guide for Evidence — a hands-on data analytics tutorial that walks you through building a business report example from scratch, taking you from an empty report to a published page in under 5 minutes. By the end, you'll build a dashboard and have a working business intelligence example you can share with your team.
Cli Index (`docs/cli/index.mdx`)
title: 'CLI'
The Evidence CLI lets you run and develop Evidence projects locally from the command line.
It includes a development server for previewing your reports locally as you work, commands for running queries, viewing metadata, and validating your code.
If you are going to use the CLI for development, you should set up the GitHub Integration.
Install
Installs to `/usr/local/bin` (falls back to `~/.local/bin` if not writable).Installs to `%LOCALAPPDATA%\Evidence\bin` and adds it to your user PATH. Restart your terminal after install.Both evidence and evd commands are available after install.
Supported Platforms
- macOS arm64 (Apple Silicon)
- macOS x64 (Intel)
- Linux x64
- Linux arm64
- Windows x64
Verify Installation
evidence versionGetting Started
- Create a project in Evidence Studio and connect it to a GitHub repo.
- Clone the repo to your local machine.
- Log in to your Evidence account via the CLI:bash
evidence login - Start developing from inside your cloned project directory:bash
evidence dev - Open a pull request on the repo. Evidence Studio will reply with a preview link. Merging the PR releases your changes.
Upgrade
evidence upgradeThe CLI checks for updates on startup. If a newer version is available, you'll see a warning. If your version is below the minimum supported version, the CLI will require an upgrade before running.
Telemetry
The CLI reports anonymous usage so we can tell how many people use Evidence, which commands matter, and which versions are still in the wild. Nothing about your data, queries, file paths, or repository is ever sent.
Opting out
Any one of these turns telemetry off:
evidence telemetry disable # persists in ~/.evd/telemetry.json
export EVIDENCE_TELEMETRY_DISABLED=1
export DO_NOT_TRACK=1 # the cross-tool conventionevidence telemetry status shows the current state and why. Telemetry is also skipped automatically on CI (CI, GITHUB_ACTIONS, GITLAB_CI, CIRCLECI, BUILDKITE, TF_BUILD, JENKINS_URL).
What is sent
One cli_command event per invocation, one cli_installed event from the install script, and — for evidence serve only — a serve_heartbeat once every 24 hours so long-running deployments stay counted, plus a serve_preflight_failed event (with a reason such as no_connection_yaml) when serve refuses to start, so we can spot broken self-host deployments.
| Field | Example | Purpose |
|---|---|---|
machineId |
random UUID | Stored in ~/.evd/machine-id on first run (or in your temp dir if home isn't writable); counts developer machines without knowing who you are |
id_source |
home / tmp |
Where the machine id was stored |
command |
dev |
Which command ran |
version |
0.9.3 |
CLI version |
os, arch |
darwin, arm64 |
Platform mix |
connector |
direct / managed |
Whether the project has a connection.yaml or uses the managed warehouse |
logged_in |
false |
Whether a Studio login exists on this machine |
project_id |
random UUID | telemetry.id from evidence.config.yaml; counts deployments of a project rather than restarts of a container |
agent |
cursor |
Set when a coding agent (Cursor, Claude Code, Codex, Gemini) is driving the CLI |
tty |
true |
Whether a person is at an interactive terminal |
runtime |
docker / bare |
Whether the CLI is running in a container |
first_run |
true |
Whether this is the first run on this machine |
sandbox |
false |
First run inside an agent or container; these never count as users |
instance_id |
random UUID | Per-process id, used only to count restarts of a deployment |
Requests carry your IP like any HTTP request; it is used for country-level geolocation only.
Project id
evidence init writes a random telemetry.id into evidence.config.yaml. It identifies the project, not you or your machine, and is what lets us count a self-hosted deployment once rather than once per container restart. Delete the key to opt a project out of that count.
When you are logged in
Commands that talk to Evidence Studio (launch, link, publish, models, and others) are authenticated API calls, and Studio records that your account and organization used the CLI, the same way it records use of the web app. Anonymous local usage is never linked to an account.
Mcp Overview (`docs/mcp/overview.mdx`)
title: 'Overview'
What is MCP?
The Model Context Protocol is an open standard that enables AI applications to connect to external data sources and tools. Evidence provides two MCP servers.
Which one do I want?
- Querying your own data and reports? Use the Studio MCP — it connects to your live workspace with your permissions.
- Developing reports? Use the Docs MCP — it searches the docs and component reference.
Self Host Index (`docs/self-host/index.mdx`)
title: 'Self Host'
Evidence projects can be self-hosted with the official Docker image.
The image runs evidence serve, which is similar to evidence dev but hardened as a production server.
Self-hosted projects must use a direct connector (configured in connection.yaml).
Docker containers can run on many platforms, major cloud providers or your own machine. See the below guides for some tested options:
Should you self host?
Self hosting is appropriate if:
- Your organization requires data does not leave your infrastructure
- You have a hobby project and do not want to pay for a managed service
However, you should be aware that there are tradeoffs associated with self hosting Evidence projects:
- Several platform features (e.g. User Auth, Access Controls, Evidence Agents) are not built in
- There are no platform fees, but you will incur costs from infrastructure and maintenance
- You need to handle security, monitoring, and scaling on your own
Generic Docker Deployment
Details may differ, but these steps are required for all Docker deployments.
Add a
Dockerfile:docker filename="dockerfile"FROM evidencedev/serve:latest COPY --chown=evidence:evidence . /projectMove secrets out of
connection.yaml, replacing them with${VAR}environment referencesCommit
connection.yaml(it is gitignored by default)Add environment variables
Env Var Purpose Required? EVIDENCE_BASIC_USERHTTP Basic Auth username Yes* EVIDENCE_BASIC_PASSWORDHTTP Basic Auth password Yes* ${VAR}referencesAll variables referenced in connection.yamlYes EVIDENCE_AUTH_DISABLEDSkip auth entirely on trusted private networks - PORTPort the server listens on (default: 3000) - EVIDENCE_TELEMETRY_DISABLEDTurn off the anonymous startup event and daily heartbeat (details) - * Unless auth disabled
Start the server
Self Host vs. Evidence Studio
Self-hosting allows you to render your SQL + markdown into interactive reports — but the Evidence Studio platform has many additional features:
| Feature | Self Host | Evidence Studio |
|---|---|---|
| Rendering reports | ✓ | ✓ |
| Direct connectors | ✓ | ✓ |
| Evidence Warehouse | — | ✓ |
| SQL models | — | ✓ |
| Auth | HTTP Basic Auth | SSO with viewer, developer & admin roles |
| Access controls | — | ✓ |
| Row-level security | — | ✓ |
| Developer Agent | — | ✓ |
| Viewer Agent | — | ✓ |
| Web editor & real-time collaboration | — | ✓ |
| Embedded analytics | — | ✓ |
| Studio MCP server | — | ✓ |
Authentication
Self-hosted sites are protected by HTTP Basic Auth. The browser caches credentials in memory until the browser is exited (there is no logout button).
To force all viewers to re-authenticate, change EVIDENCE_BASIC_PASSWORD and redeploy.
For deployments only reachable over a trusted private network (VPN, Tailscale, internal VPC), set EVIDENCE_AUTH_DISABLED=true to skip authentication entirely.
To authenticate users against your organization's identity provider, deploy evidence serve behind an authenticating reverse proxy — see Authentication & SSO.
Changelog (`docs/changelog.mdx`)
title: 'Changelog' rss: true
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
✨ AI chat in embedded reports
Embedded reports can now show the Evidence AI chat, so your users can ask questions about a report without leaving your application. Embedding customers opt in per session with the enableAIChat parameter on the embed API.
🐛 Organization switching for viewers
Fixed an issue that blocked viewers from switching organizations in the published app.
🚀 Big values fit two to a line on phones
On phones, big values now sit two to a line instead of stacking one per line.
🚀 Render html blocks without a card
On pages with card mode enabled, {% html card=false %} renders the block directly on the page background instead of inside a card.
✨ Preview pages at mobile width in the editor
A new toggle on the editor's preview pane narrows the preview to phone width, so you can check how a page reads on mobile without resizing your browser.
- Mobile-specific behavior shows in the preview too:
detailshonorsopen_mobile, and modals open as they would on a phone, inside the preview. dimension_gridnow wraps based on its own width rather than the browser's, so it lays out correctly in narrow columns as well as on phones.
🐛 Stacked areas no longer have holes
Fixed gaps in stacked area charts on date axes when a series has no data for some dates.
✨ Hide or show content in print without grouping it
New {% hidden_in_print %} and {% only_in_print %} blocks control what appears in printed pages and PDFs, without the blank gaps print_group can leave.
🐛 Reopening an invitation link takes you to your organization
Reopening an invitation you've already accepted while signed in now takes you into that organization instead of showing an error.
🐛 Improved PDF exports
Fixed charts that could be missing from PDF exports.
✨ Commentary per input selection
Commentary can now be kept separately for each selected value of one or more inputs, alongside the reporting period.
✨ Page download options
Hide download options from a published page's menu with the downloads page setting.
✨ Period selector component
Added a period selector input, which can also place a page's reporting period anywhere on the page.
✨ Print-only content
Show content only in printed pages and PDFs with {% print_group print_only=true %}.
🚀 Improved row layout
Rows can now spread their contents across the full width, for example a heading on the left and a button on the right.
✨ Generate Excel Report
A new Generate Excel Report option exports the page as an Excel workbook with a snapshot of the report on the first sheet and a data sheet per component; Download Data now also appears in the CLI.
- Download Data is unchanged and still exports data only; pick Generate Excel Report when you want the report snapshot included.
- Hide it with
downloads: { excel: false }in page frontmatter. It is also hidden whenimageordatadownloads are turned off. - The local CLI viewer now has Download Data and Generate Excel Report in its page menu.
🚀 Improved info tooltips
Info tooltips now support text across multiple lines.
🐛 Improved date axis labels
Improved the layout of date labels on chart axes.
✨ Attach files to reports
Drop or paste a file into the editor to upload it and insert a file card showing its icon, name, type, and date.
🚀 Set down_is_good for a whole page
Add down_is_good: true to a page's frontmatter to treat decreases as good for every delta on that page.
- Applies to
big_value,delta, and table delta columns. A component's owndown_is_goodstill overrides it. down_is_good=trueon abenchmark_comparisonortarget_comparisonoption is now respected when a component uses thatcomparison_selector.
🐛 Tables inside repeat blocks now read page inputs
A table inside a {% repeat %} block now resolves page inputs referenced in its where, having, order, and qualify attributes instead of running with the value missing.
- Previously the table only looked at the repeat's own scope, so
{{my_input}}in these attributes interpolated as empty and the query failed with a syntax error. - Titles and other text attributes on the same table also resolve page inputs correctly now.
🐛 CLI runs direct-connector projects without reaching Evidence Studio
evidence dev no longer refuses to start when evidence.studio is unreachable for projects with a connection.yaml, evidence upgrade now works on Windows, and the home page honours page_width and cards like every other page.
- Projects with a
connection.yamlskip the Studio reachability check entirely; projects on the Evidence Warehouse get a warning (with the failure reason and a proxy hint) instead of a hard exit. evidence devprints which mode it is in — direct connector or managed warehouse — so it is clear whether a login is needed.evidence upgradesupports Windows x64 and Linux arm64, keepsevd.exein sync on Windows, and points to the installer when the update check fails.evidence serveis listed inevidence help.pages/home.mdnow respectspage_width,cards,table_of_contentsand frontmattertheme, matching subpages.
🐛 Viewer log out fix
Fixed logging out from a published report showing a "Page not found" error and leaving you signed in.
🚀 Details start state on mobile
The details component has a new open_mobile attribute to choose whether it starts open or collapsed on mobile screens, independently of open
✨ Validation summary in the editor
A new indicator in the editor toolbar shows how many validation errors are on the open file. Click it to see every error with its line number, and click an error to jump to it in the editor.
- Covers component errors, inline SQL query errors, and config file (yaml) validation.
- Each entry is labeled with the component, query, or config key it belongs to.
- Clicking an error scrolls the editor to the line and highlights it.
🐛 CLI keeps input selections in the URL
Input components in evidence dev and self-hosted CLI pages now write their selections to the URL and restore them from it, so filtered views can be refreshed, bookmarked and shared like in Studio.
🐛 Commentary save fix
Fixed an issue where some commentary saves did not persist.
🐛 Filter defaults no longer appear in URL
A table_filter with initial_values no longer writes its default into the page URL, so other pages keep showing their own data instead of inheriting that default and filtering against the wrong values.
✨ Commentary supports workflow periods
Commentary components can now keep separate notes for each reporting period with scope="period".
- Period-scoped commentary follows the page's
workflow.periodpicker, including in PDF renders. - Commentary without
scoperemains page-wide.
✨ Waterfall chart
The waterfall_chart component is now available - useful for growth decomposition, revenue bridges, and reconciliations
✨ Search team members
Filter your team settings page by name or email with a new search field.
🐛 Slack channel picker finds every channel the bot is in
Fix the scheduled report Slack channel dropdown missing channels the Evidence bot had joined in large workspaces with many channels.
🚀 Improved content selection in PDF download menu
Simpler click behaviour to select and deselect content in nested sections.
🐛 Table cells only merge within their parent group
Fix table dimension cells merging across rows whose parent dimension values differ, such as after sorting by a measure.
🚀 Resume GitHub Enterprise connections
GitHub Enterprise Cloud connections can now resume after another organization owner installs the app, with clearer errors and controls for unfinished attempts.
- Finish connecting from GitHub integration settings when the installation callback is missed.
- Allow up to seven days for installation after app registration.
- Resume or cancel an unfinished connection instead of creating another app on each retry.
- See an error message and request ID when a connection needs support.
✨ Reporting periods
Turn a page into a periodic report with a workflow.period block in frontmatter.
- Adds a period picker at the top right of the page, listing complete calendar periods (day, week, month, quarter or year)
- The selected period is available as the
periodvariable —{{ period.label }}in markdown,{{ period.between }}in SQL - Only finished periods are offered, so a published report doesn't change while its period is still running
- The selection is kept in the URL, so a link points at a specific period
🐛 Dropdown options on Cube
Fix data-driven dropdowns returning no options when connected to a Cube warehouse.
✨ Scheduled report using email or Slack
Scheduled report using email or Slack
✨ More control over color scales
Color scales in tables and map layers now support diverging midpoints and custom value-to-color breakpoints, and behave consistently across both.
- Set
midpointto anchor a diverging scale at a value such as 0, andmin/maxto fix the scale bounds. - Use
color_stopsto pin specific colors to specific values. - Diverging scales stay correct even when every value falls on one side of the midpoint.
🚀 Improved error display
Components now display syntax and query errors more prominently in the editor.
🐛 Reliable map layer filter updates
Area, point, and heatmap layers now reliably update when filters change their data.
🐛 Table filter values on Snowflake
Fix the table filter returning no selectable values on Snowflake and other uppercase-folding warehouses.
✨ Folder-Level Access Rules
Add folder-level access rules to access.yaml — grant or restrict every page in a directory subtree with a single entry.
✨ Scheduled Reports
Email reports to your users on a regular schedule, or send one-off reports on demand.
✨ Access Viewer AI inside Slack
Connect a Slack workspace in Integration settings and message the Evidence bot to get answers from your data without leaving Slack.
- An admin adds the app from Settings → Integrations → Slack, then each person connects their own account — the bot only reads what you can already see.
- Direct message the bot, or @mention it in a channel.
- Answers can display charts, save insights to your library, and link back into Evidence.
- Admins can turn the bot off for the whole organization, and choose whether each person's first answer in a channel is shown to them before the channel sees it.
🐛 Partial filter initial values now apply on parent pages
Fixed an issue where filter initial values defined in partials could be empty when rendered on parent pages.
🐛 Filters load in partial page previews
Filters in partials now load when using View another page in the editor.
🐛 Link buttons support relative page URLs
Link buttons now navigate correctly with portable relative URLs across edit, preview, and published reports.
🐛 Table filters close safely without selections
Table filters now remain visible when a numeric or date picker is closed without a selection.
🐛 Sorting a table by a column it does not show
Sorting a table by a column that is not one of its dimensions or measures no longer splits the rows into extra groups, and no longer fails on Postgres, Fabric and Cube.
🐛 ClickHouse conditional aggregates
Fix sumIf and its -If siblings breaking date-filtered measures and comparison targets on ClickHouse, and no longer warn that they are missing an aggregation.
🐛 Snowflake table conditional colors
Fix conditional colors on table dimensions in Snowflake for literal colors and SQL expressions.
🐛 Sorting a table by a grouped date column
Sorting a table by a date column that has a date_grain now sorts by the grouped date instead of failing with a grouping error.
order="my_date desc"alongside{% dimension value="my_date" date_grain="day" /%}sorted by the raw column, which the warehouse never grouped. ClickHouse rejected the query and the table rendered empty.- A hidden sort column on a table with subtotals is now aggregated, so it no longer breaks the subtotal and total rows.
🐛 Creating files and folders is disabled on protected branches
The sidebar's create page, partial, SQL, component, folder, and agent-file actions are now disabled when the current branch is protected on GitHub, matching the existing branch-protection guard on Commit.
✨ Bring your own model API key
Enterprise organizations can now point both AI agents at their own OpenAI, Anthropic, AWS Bedrock, or Google Vertex account from Settings → AI Agent.
- Both the editor agent and the viewer agent use your provider and model, so your provider bills you for inference directly and Evidence AI credits are neither consumed nor charged.
- Credentials are stored in the WorkOS Vault and never sent back to the browser. Saving verifies the credential against your provider first, so a wrong key or model ID is rejected rather than stored.
- There is no fallback to Evidence's models: if your credential stops working, chat reports a clear error instead of quietly switching back and billing you for credits.
🚀 Big value titles are clickable when linked
Setting link on a big value now turns the title into a styled link instead of the number, matching typical score card conventions.
✨ Metrics
Evidence now has a metrics layer. Define metrics in metrics/*.yaml and reference them in charts and tables. Ask the agent to build one for you to get started.
✨ MCP Visualize Tool
The Evidence Studio Claude Connector and ChatGPT Plugin can now generate charts inline in the chat with the new visualize tool.
✨ Reports page for run history and schedules
A new top-level Reports page (next to Models) for viewing report run history and managing schedules.
- History tab: View all report runs with status, trigger type (manual/schedule/API), author, timing, and recipient count
- Schedules tab: View and manage recurring report schedules with pause/resume and delete actions
- Admin-only actions: Pause/resume schedules, delete schedules, re-run past reports, download PDFs
- Developer access: Developers (members) can view the page; admins can take all actions
- Entitlement gate: Available on Team, Pro, and Enterprise plans
✨ GitHub Enterprise Cloud support
Enterprise organizations can now connect Evidence Studio and the Evidence CLI to GitHub Enterprise Cloud accounts hosted on dedicated *.ghe.com domains.
- Create and install a private, organization-owned GitHub App through Organization Settings.
- Select accessible enterprise repositories in Project Settings and use Studio branches, pull requests, checks, and publishing as usual.
- Run
evidence launchorevidence linkfrom HTTPS and SSH GHE.com remotes.
🐛 Feature request dialogs remain accessible with long descriptions
Feature request dialogs now constrain long descriptions to a scrollable area so their controls remain accessible.
🐛 Table links now work in fullscreen reports
Table links now navigate to their destination while viewing a report in fullscreen presentation mode.
✨ MotherDuck direct connector
Connect Evidence directly to a MotherDuck database to build reports on live data without syncing it first.
✨ Custom Maps
Use the custom_map component to build fully customized maps with Mapbox GL or MapLibre GL.
- Write map code directly in the component body — pull page data with
evidence.query, read and write page filters, and react to variables and theme changes. - No key needed by default: keyless MapLibre out of the box, or Mapbox using Evidence's shared token under the hood.
- Import map-ecosystem plugins (mapbox-gl-draw, deck.gl, turf, and more) straight from a CDN.
🐛 Custom HTML and ECharts blocks work in the CLI
{% html %} and {% custom_echart %} blocks now render when running a project locally with the Evidence CLI.
- Both blocks previously rendered blank in
evidence dev— the sandbox runtime they load was only built by the hosted app, so the CLI served nothing for it - Requires CLI v0.7.4 or later
- If a sandbox runtime is ever missing, the block now reports an explicit error instead of staying blank
🚀 AI chat now consumes far fewer credits
We've significantly reduced how many credits AI chat consumes, so your balance goes much further.
✨ Cube Connector
Connect Evidence directly to your Cube semantic layer through its Postgres-wire SQL API.
- Query your cubes and views live from Evidence — metrics stay defined in Cube.
- Configure it in Connectors → Warehouse → Cube (host, port, user, password, SSL), or via
connection.yaml(type: cube) in the CLI. - The SQL editor validates expressions against Cube's documented function subset, so unsupported functions are flagged before you run a query.
✨ PDF "Use page colors" option
Preserve the exact colors from your page when generating PDFs.
- New "Use page colors" checkbox in the PDF settings modal (Cmd+P)
- When enabled, PDFs retain the theme's actual background colors (including dark mode or custom theme backgrounds)
- When disabled (default), PDFs use white backgrounds optimized for printing
- Preview updates to show how the PDF will look with the selected option
✨ Postgres Direct Connector
Connect Evidence directly to Postgres — including Amazon RDS/Aurora, Supabase, Neon, and Timescale — and query it live.
- Add Postgres under Settings → Warehouse: enter host, port, database, user, password, and SSL mode, test the connection, and save.
- Works with any Postgres-wire source: generic Postgres, Amazon RDS / Aurora, Supabase, Neon, and Timescale.
- Full SSL/TLS support with
disable/require/verify-ca/verify-fullmodes, plus an optional CA bundle and mutual-TLS client certificate. - The schema browser lists tables, views, and materialized views.
- Also available from the CLI via
connection.yaml(type: postgres) orevidence init --warehouse postgres, with certificates referenced by file path.
✨ Search across your reports with Cmd+K
Press Cmd/Ctrl+K to instantly search across every report you have access to.
✨ Viewer AI & Insights
Report viewers can now ask questions of your data in a chat assistant and save the answers as insights to revisit later.
🐛 Fixed editor inserting characters at the wrong position on older reports
Typing in the editor now appears where you clicked on reports created before mid-2026.
✨ Evidence Studio MCP Server
Search your reports and query your data from your favorite AI client. Connect Claude, ChatGPT, and other MCP clients to your live Evidence workspace — every request runs as the signed-in user, so access policies and page controls are respected.
🐛 CLI dev server no longer crashes when a subfolder has an index.md
Nested index.md / home.md / README.md files are no longer treated as the home page in the sidebar, so evidence dev renders normally instead of crashing with a duplicate-key error when a project has a root home page plus an index.md in a subfolder.
✨ Point Map Clustering
You can now include up to 100,000 points in a point layer by using cluster=true. Nearby points are grouped into aggregated bubbles that split apart as you zoom in, so large datasets stay fast and readable.
✨ 50 New Data Connectors
Connect 50 more data sources to Evidence, including Zendesk, Intercom, Mixpanel, Amplitude, Square, QuickBooks, Recurly, Pipedrive, Gong, LinkedIn Ads, and more. Set them up in a few clicks from the Connectors page.
✨ 50 More Data Connectors
Another 50 data sources are now available, including Jira, Airtable, Asana, Notion, Slack, Klaviyo, Mailchimp, Braze, SendGrid, TikTok Ads, Amazon Ads, Google Search Console, ServiceNow, and Okta. Set them up in a few clicks from the Connectors page.
✨ Lemlist Connector
Connect your Lemlist data to Evidence. Sync campaigns, leads, and activities from your email outreach.
🚀 Improved chart x-axis and tooltip labels
Better handling of dates and numbers on chart x-axes and tooltips.
✨ Table Access Restrictions
Restrict tables to a chosen set of users
Restrict specific tables to a chosen set of users from the Access Rules settings page. Unrestricted tables stay open to everyone in the organization, and restricted tables show a lock badge in the editor's schema list.
✨ Image component accepts data queries
Add data and column props to the image component to load the image URL from a query and respond to filter changes — e.g. show the top-selling product's photo, a client logo driven by a dropdown, or a dark-mode variant via dark_column.
✨ Radar, Treemap, and Chord charts
Three new charts now available: radar_chart, treemap, and chord_chart
✨ Mix single and multi select columns in table filters
Add single_select and multi_select to table_filter to set the selection mode per column — e.g. restrict a client column to one value at a time while keeping region and product filters multi-select.
🐛 Y-axis titles stay legible above chart data
Chart contents no longer overlap the y-axis title — bars, areas, and lines that reach the top of the axis now render beneath the title instead of covering it. Gridlines are cut off cleanly behind the title rather than showing through the text, the title is vertically aligned with the top axis tick label, and a subtle text outline keeps it readable when data passes directly beneath it. Applies to both left and right (y2) axis titles.
🐛 range_calendar "all time" no longer breaks raw SQL
Referencing {{ date_filter.between }} in a where or SQL template while "all time" was selected previously produced an empty string, which broke the query (where date → syntax error). .between now emits IS NOT NULL for "all time", so where="date {{ date_filter.between }}" matches all rows and just works.
The new all_time_range attribute controls what "all time" means for a specific range_calendar:
all_time_range="none"restores the previous empty-string behavior — pair it with[[ ]]conditional blocks or a| fallbackwhen the whole clause should drop (e.g. to keep NULL-date rows, whichIS NOT NULLotherwise excludes).- Set it to a bounded range — a preset key (
"last 12 months"), acustom_rangeslabel, or a range expression ("from 2022-12-01") — and "all time" resolves to that range everywhere (.between,.start,.end, and thedate_rangevalue). If you were faking this with a| fallbackin yourwhereclause, setall_time_rangeto that range and delete the fallback.
.start / .end stay empty for unbounded "all time" (so date spines and axes fail loudly rather than silently wrong), and .filter / a filters=[...] reference stay a true no-op that keeps NULL rows.
🐛 Fix for AI chat losing conversation context
Fixed an issue where the AI chat could respond to an earlier message in long conversations instead of the one you just sent.
🐛 Sparkline hover values now use theme foreground
Sparkline hover values and the vertical tracking line follow the theme's foreground and muted-foreground tokens, restoring contrast in light mode (most visible on big_value sparklines).
✨ AI Agent Multi-File Edits
The AI dev assistant can now edit multiple files in a single turn — as well as create, rename, and move files.
✨ Extra tooltip rows on chart series
Add tooltip_fields to any bar_chart / line_chart / area_chart / scatter_chart / bubble_chart — or to bar, line, area, scatter, bubble inside a combo_chart — to surface additional aggregated values on hover, with optional labels, format codes, and color_by_sign / down_is_good for delta-style coloring.
✨ Link charts together
Add connect_group to charts to sync their tooltips, axis-pointer, and zoom across any charts sharing the same id — e.g. stack a price chart above a volume chart, align KPIs that share a time axis, or coordinate small multiples.
✨ Customize built-in charts with ECharts options
Override any ECharts option on built-in charts via echarts_options (chart-wide) or echarts_series_options (looped across every series).
✨ Attach images to editor AI chat
Click the image button in the editor chat composer to attach screenshots or other images for the AI agent to see alongside your message.
✨ Custom components
Build your own Markdoc tags from components/*.md files — each component declares its attributes in frontmatter and gets autocomplete, slash-command, and AI-tool support just like a built-in.
✨ Pin color scale values in tables
Anchor a measure's color scale to specific data values with color_stops.
✨ Custom HTML in reports
Embed your own HTML and JavaScript anywhere in a report with the new {% html %} block. Build custom visualizations that integrate with Evidence's filters, queries, and theme — using any library, like D3 or deck.gl.
🐛 Hide unneeded subtotal rows in tables
Tables now hide duplicative subtotal rows that only summarize a single item, while keeping the grand total.
✨ Expanded theme options
Themes now include fonts, colors, corner roundness, spacing density, and chart defaults, applied consistently across the whole report. Override the theme for a single page when you need to.
✨ Custom date ranges
Add custom_ranges to {% range_calendar %} to generate named presets — e.g. fiscal years, seasons, and retail calendars.
🐛 Smoother schema panel refresh
The refresh button no longer pushes content down or hides the schema list while reloading, and long table or column names truncate instead of forcing horizontal scroll.
✨ Show size dimension in bubble and scatter chart tooltips
Bubble and scatter chart tooltips now display the size dimension value, with a new size_fmt attribute for formatting.
🐛 Faster, more reliable editor preview on heavy pages
Fixed an issue where the editor could lock up on pages with many charts and tables.
🐛 Fix heatmap collapsing at wider screen widths
Fixed heatmap charts collapsing into a small box at certain screen widths.
✨ Impersonate
Create and manage access tokens for programmatic API access from the new Access Tokens settings page. Admins and developers can impersonate team members to see exactly what they see (disabled by default).
🐛 Fix dropdown search failing on BigQuery
Fixed an issue with searching a dropdown or table filter for BigQuery direct connections.
✨ Failed Publishes No Longer Affect Viewers
If a publish fails, your project keeps serving the last successful version — viewers never see broken content.
🐛 Fix spurious markdown validation errors on PRs
Fixed the PR markdown-validation check reporting false-positive undefined-translation-key and undefined-frontmatter-variable errors on pages using translations and on partials that receive variables from the pages including them.
✨ Publish History & Rollback
View your project's publish history and roll back to any previous version.
✨ ClickHouse direct connector
Connect Evidence directly to your own ClickHouse instance to build reports on live data without syncing it first.
✨ Make the selected option in `button_group` and `input_tabs` more noticeable
The selected button in button_group and the active tab in input_tabs now stand out more clearly.
✨ New share menu and additive page access
The redesigned share menu lets you grant page-level access on top of project access, so you can share a single dashboard with a stakeholder who shouldn't see anything else.
✨ Vibe code any visualization with the custom_echart component
Need a chart Evidence doesn't have out of the box — a waterfall, a bridge, a radar, something bespoke? The new custom_echart component lets you build any visualization you can imagine. Describe what you want to the assistant and it writes the chart for you, wired up to your data, filters, and theme automatically.
✨ Pre-select the top N options in a multi-select dropdown
Multi-select dropdowns now accept a default_top_n attribute that pre-selects the first N options on load. Combine it with order to control which N are selected by default — which viewers can then change.
✨ New Publish Menu in Editor
View pending changes and open pull requests directly from Studio.
✨ See what each user, group, and customer can access
Admins and developers can now see every project and page a user, group, or customer can access, plus how that access was granted.
🐛 Share menu now lets you grant page access to any team member
The Page tab of the share menu now lists every internal team member, so you can grant page access directly without first adding them to the parent project.
🚀 Add users to groups when inviting, and see group memberships in the team table
Admins can now pick groups when inviting or manually creating users, and the Team settings page shows each member's group memberships in its own column.
🚀 HubSpot Connector
Updated the HubSpot connector with more reliable syncs and broader table coverage.
✨ Microsoft Fabric direct connector
Connect Evidence directly to a Microsoft Fabric Warehouse or Lakehouse SQL endpoint to build reports on live data without syncing it first.
🐛 PDF exports preserve dropdown filter values
PDF exports of pages with a select_first=true dropdown no longer revert the filter to its auto-selected default when the URL specifies a different value.
✨ Map area layer min, max, and midpoint
Map area_layer and point_layer now accept min, max, and midpoint props so you can pin a diverging color scale at zero (or any other value).
🐛 Fixed bar chart tooltip not displaying point_title
Fixed an issue where the point_title attribute on bar, line, and area charts was ignored in the tooltip.
✨ File change indicators in editor sidebar
Files with uncommitted changes now show colored status badges (M/A/D) in the editor sidebar file tree, so you can see which files have been modified without switching to the Version Control tab.
✨ Copy link to pages
Copy a shareable link to the current page from the page menu, with option to include the current filters.
✨ Download report as PNG
Save a full page as a PNG image from the page menu.
🚀 Dedicated Groups settings page
Groups now have their own page under Settings → Groups, separate from team members, with a detail view for managing each group's members and access.
✨ Snowflake and BigQuery direct connectors
Link Evidence to Snowflake or BigQuery directly to build reports without syncing data.
🚀 Improved discoverability of date input components in AI chat
Added search keywords to range_calendar, date_grain_selector, and comparison_selector so the AI assistant can find them when users ask for "date range filter", "date picker", "date input", etc.
🚀 Long chart label rotation
Charts now rotate long x-axis labels automatically so category names are easier to read without hovering.
🐛 Map area layer filter updates
Fixed a bug where area charts did not respond to filter changes.
🚀 Data download title filenames
Data downloads now use titles in file names.
🐛 Horizontal bar chart axis bound validation
The horizontal_bar_chart now reports a validation error when x_axis_options.min is greater than or equal to x_axis_options.max.
✨ Evidence CLI now available on Windows
The Evidence CLI now ships a Windows x64 binary, installable via PowerShell: irm https://gaamozau3jchzs3r.public.blob.vercel-storage.com/cli/install.ps1 | iex.
🚀 Default tab selection
Add default=true to a {% tab %} to pre-select it on page load instead of always defaulting to the first tab.
🐛 Data labels render on area and line charts
Fixed data_labels not appearing on area_chart, line_chart, and combo_chart line/area series.
✨ Add today and yesterday range calendar presets
Range calendars can now opt into Today and Yesterday preset ranges.
🚀 Faster Page Create and Rename
Creating, renaming, and reorganizing pages and folders in the editor sidebar is noticeably faster.
🐛 Mobile sidebars close after navigation
Fixed mobile sidebars staying open after selecting a navigation link.
🐛 Improved SQL file support in GitHub integration
Improved how SQL files sync between Studio and GitHub.
🐛 Branch-aware link autocomplete and validation
Editor link autocomplete and validation now include pages that exist only on the current branch, and AI chat no longer flags them as broken.
🐛 AI chat current date awareness
Fixed AI chat date context so the agent knows today's date when answering in editor and viewer chats.
🐛 Locally-authored pages now appear in Studio
Fixed an issue where files created locally and synced to GitHub were not appearing in Studio.
🐛 Stable heading logo sizing
Fixed an issue where logos inside headings could briefly render at the wrong size on page load.
🐛 Fix unquoted variables in partial attributes
Fixed an issue where unquoted variable references in component attributes failed when rendered from partials.
🐛 Fix x_sort="data" imposing an x-axis sort
Fixed an issue where setting x_sort="data" on bar and line charts still sorted by the x-axis instead of preserving the source query's row order.
🐛 Embedded page centering without TOC
Fixed an issue where embedded pages could stay shifted to the left after the table of contents was turned off.
🐛 Stable logo sizing on load
Fixed an issue where logos in headings could briefly render at the wrong size while the page was loading.
🐛 Improved models table readability
Fixed an issue where the models table was not sorted by name by default and long descriptions were hard to scan.
✨ Select content for PDF exports
Choose which sections and components to include when exporting a PDF.
🐛 Source failure modal fix
Fixed an issue where opening a failed source status could show a blank blocking modal instead of the failure details.
🚀 Longer AI chat sessions
Increased the maximum AI chat response time so longer agentic conversations no longer time out.
🚀 Collapsible projects in published and preview sidebars
Projects in the sidebar can now be collapsed and expanded by clicking the project name, with state persisted across sessions.
🚀 Table Actions
Download and Fullscreen buttons now available in Table footers.
🚀 Claude Opus 4.7
The Evidence agent now uses Claude Opus 4.7 by default.
✨ Connector renaming
You can now rename connectors directly from the connectors page or the connector detail page by clicking the pencil icon next to the name.
🐛 Dropdown search now handles nullable values safely
Fixed an issue where searching in a dropdown could fail when the value or label column contained empty values.
✨ Data lineage
See where your connectors and tables are used across projects, pages, models, and queries. Available in the connectors page, connector detail pages, models page, AI chat, and the CLI.
🚀 Resizable SQL console schema panel
The SQL console schema panel can now be resized by dragging its edge so long table names are easier to inspect.
✨ Connector and model health status
Connectors page now shows health status and sync schedule for all connector types. New connectors and models CLI commands let you check health statuses from the command line.
🐛 Fixed theme settings loading state while editing colors
Fixed an issue where editing theme color values could cause a broken loading state in settings.
🐛 Fixed inline SQL editor query references
Fixed an issue where inline SQL editor queries could fail when referencing other inline queries.
🐛 Fixed unexpectedly large branch diffs
Fixed an issue where small edits in Git-connected branches could appear as much larger diffs than expected.
🐛 Fixed stacked combo charts on time-based axes
Fixed an issue where stacked combo charts on time-based axes could render blank or incorrect series data.
🐛 Fixed filter bar not floating on scroll in published pages
Fixed an issue where the filter bar would not stay pinned to the top of the page when scrolling in published mode.
✨ Usage Analytics
Track how your team uses Evidence with built in analytics tables for sessions, users and page views.
🐛 AI theme updates apply instantly
Fixed an issue where theme changes made by the AI assistant only appeared after a page refresh.
✨ Audio Component
Embed an audio player in your reports with {% audio url="..." /%}. Supports mp3, wav, ogg, and other formats, with options for looping, autoplay, and preload behavior.
🐛 Calendar heatmap dark mode colors
Fixed an issue where calendar heatmaps could show white empty-day cells in dark mode instead of blending with the page or card background.
✨ Conditional colors for dimensions and rows
Apply conditional background colors to entire rows or individual dimension cells using SQL expressions.
🐛 Fixed table SQL error overlay spacing
Fixed an issue where table SQL errors could overlap content below the table.
🐛 Expanded background color options for AI theme updates
Expanded the background color options the AI agent can change in theme updates.
✨ YouTube Analytics Connector
Connect your YouTube Analytics data to Evidence. Sync channel reports, content owner reports, and metadata tables.
🐛 Fixed combo chart SQL with toggle-driven inline queries
Fixed an issue where combo charts could fail when inline queries used toggle values in SQL conditions.
✨ Multiplayer editor
Collaborate with your team in real-time with live cursors and presence indicators.
✨ Axis titles for heatmaps
Heatmaps now support x_axis_title and y_axis_title so you can label both axes directly on the chart.
🐛 Fix for table column groups with link attribute
Fixed an issue where adding a link attribute to a table dimension would cause all column groups to disappear.
🐛 Fixed spacing in the back-to-partial label
Fixed an issue where the "Back to partial" label appeared without a space in preview mode.
🐛 Horizontal bar chart axis min/max now apply exactly
Fixed an issue where x_axis_options.min and x_axis_options.max on horizontal bar charts could be ignored instead of enforcing the requested axis range.
✨ SAP HANA Connector
Connect your SAP HANA data to Evidence. Sync data from SAP ECC and SAP S/4HANA systems running on the HANA platform.
🐛 Fix slider lockup with invalid step values
Fixed an issue where sliders could freeze the page when configured with an invalid step value.
✨ Third-party analytics for published pages
You can now add your own Google Analytics (GA4) or PostHog tracking to published and embedded pages. Configure your analytics provider in Organization Settings under Third-Party Analytics. Available on Enterprise plans.
🚀 Collapsible folders in published and preview sidebars
Added click-to-expand and collapse behavior for sidebar folders in published and preview pages to make navigation easier in larger projects.
✨ Added This Month and Next Month date presets
Range calendars now support this month and next month presets so you can filter full calendar months including upcoming dates.
🐛 Dropdown options now support numeric values for initial selection
Fixed an issue where dropdown initial values could fail to apply when option values were provided as numbers.
🚀 Loading indicator for map components
Map components now show a loading indicator while map data is being fetched so blank maps are less confusing during slow queries.
🐛 Fix for occasional AI chat timeouts
Fixed an issue where the AI agent could appear to run out of time when working with large queries.
🐛 Fix for table search
Fixed an issue where searching in tables could produce errors or fail to recover after clearing a search.
🐛 Fix table sorting when subtotal rows are hidden
Fixed an issue where sorting by measure columns in tables could produce incorrect order when subtotal rows were hidden.
🚀 Add mmm-yy date format
Added a built-in mmm-yy format code so dates can be displayed as month and two-digit year, like Jan-22.
🐛 Fix mobile sidebar close animation flicker
Fixed an issue where the sidebar would briefly flash back into view at the end of its close animation on mobile devices.
🚀 Resizable Sidebar
The sidebar can now be resized by dragging its edge.
🐛 Comparison column formatting fix
Fixed an issue where prior period comparison columns in tables did not apply the same number format as the main value column.
✨ Evidence CLI
Develop Evidence Studio projects locally from the command line with evidence dev.
🐛 Improve filter value visibility in the editor sidebar
Fixed an issue where filter values in the editor sidebar could be hard to read by showing each value on its own line.
🐛 Recent AI chats update instantly
Fixed an issue where new AI chats would not appear in Recent chats until the page was refreshed.
🐛 Improved spacing for small tables
Fixed an issue where tables with only a few rows could show unnecessary empty space.
🚀 Clearer error when defining queries inside components
Added a validation error when inline queries are defined inside components like tabs, accordion, or details. Queries should be defined at the top level of the page.
🐛 Accordion items now maintain their original order
Fixed an issue where accordion items could reorder themselves based on when they were last updated instead of their original document order.
🐛 Support for branch names with slashes in GitHub integration
Fixed an issue where branches containing slashes (e.g., feature/my-branch) would break navigation across the editor, preview, published views, and PDF generation.
🐛 Button group and dropdown initial value fix
Fixed an issue where button groups and dropdowns could not set an initial value when using a numeric value column.
🐛 Fixed organization logo display in dark mode on published pages
Fixed an issue where the organization logo in the sidebar could appear as a dark image on a dark background when viewing published pages in dark mode.
🚀 Option to hide axis title arrow
Added a title_arrow option to x_axis_options that lets you hide the arrow (→) on chart axis titles.
🚀 Demo data table references updated
Demo data tables have moved to the demo schema (e.g. demo_daily_orders → demo.daily_orders). You may need to update references in existing projects and models.
🐛 Fixed model save failing after validation
Fixed an issue where saving a model could fail if the SQL query contained leading or trailing whitespace.
🐛 PDF downloads from editor now use working version
Fixed an issue where downloading a PDF from the editor would always produce the committed version instead of the current working draft.
🐛 Print group cards spacing fix
Fixed an issue where wrapping components in a print group with cards layout enabled would remove the spacing between cards.
🐛 Google Analytics setup links to Evidence docs
Fixed an issue where the Google Analytics connector setup page linked only to the Fivetran docs instead of the Evidence docs page.
🚀 More reliable PDF generation
Improved PDF generation reliability, especially for pages with conditionals, partials, and many components.
🐛 PDF downloads from preview now use working version
Fixed an issue where downloading a PDF from the preview route would always produce the published version instead of the working draft.
🐛 Fixed chart rendering error on published pages with point_title
Fixed an issue where charts using the point_title attribute could fail to render on published pages.
🐛 Fixed scatter chart tooltip with point_title and series
Fixed an issue where the point_title attribute on scatter charts was ignored in the tooltip when a series attribute was also set.
🐛 Fixed scatter chart size attribute
Fixed an issue where the size attribute on scatter charts was ignored, causing all points to render at the same size.
✨ Date range support for map layers
Map layers (point_layer, area_layer, and heatmap_layer) now support the date_range attribute for filtering data by time period.
✨ PayPal Connector
Connect your PayPal data to Evidence. Sync transactions, invoices, payments, subscriptions, and billing plans.
🚀 Connection string autofill for PostgreSQL connections
You can now paste a PostgreSQL connection string into any field when setting up a PostgreSQL connection, and all fields will be automatically filled in.
✨ RevenueCat Connector
Connect your RevenueCat subscription data to Evidence. Sync subscribers, transactions, entitlements, offerings, and products.
🚀 Invite team members from the Share menu
Added a quick link to Team Settings from the Share menu so you can invite new team members while managing page or project access.
🐛 Fix for dimension grid with inline queries
Fixed an issue where the dimension grid component could not use inline SQL queries as its data source.
🐛 Fix legend color for lines with explicit color in combo charts
Fixed an issue where lines in combo charts with an explicit color option (e.g., options={color="black"}) would display the palette color in the legend instead of the specified color.
🚀 Improved component search with keyword aliases
Added search keywords to component schemas so components can be found by common alternative names (e.g., "scatter plot" for scatter chart, "donut" for pie chart, "KPI" for big value).
🐛 Fix filters with multiple consecutive spaces in values
Fixed an issue where filter values containing multiple consecutive spaces would fail to match data correctly.
🐛 Fix collapsible table arrows not showing with hide_row_totals
Fixed an issue where collapsible table arrows were not visible when measures had hide_row_totals enabled.
🐛 Fix branch deletion when branch was already deleted on GitHub
Fixed an issue where deleting branches from the Evidence UI would fail with an error when the branch had already been deleted on GitHub (e.g., by GitHub's auto-delete on merge feature).
✨ Candlestick chart component
Added a new candlestick chart component for visualizing OHLC financial data with optional volume bars.
🐛 Fix for overlapping content in PDF exports
Fixed an issue that could cause overlapping content in PDF exports.
✨ Percentage labels for pie charts
Added pct and pct_fmt options for pie charts to display percentage values directly on slice labels.
🐛 Fix AI chat generating dropdowns and date range filters
Fixed an issue where AI chat could fail when generating dropdowns or date range filters.
✨ Salesforce Connector
Connect your Salesforce CRM data to Evidence. Sync accounts, contacts, opportunities, leads, and campaigns.
🐛 Fix x_fmt not applying to chart tooltips
Fixed an issue where setting x_fmt on charts would format the axis labels but not the date value shown in the tooltip.
🐛 Improved AI chat reliability
Fixed an issue where AI chat could error out during longer conversations.
✨ Date Range Support for Input Components
Input components (dropdown, button_group, input_tabs, slider, dimension_grid) now support the date_range attribute, allowing you to filter options by a specific time period or a range_calendar selection.
🐛 Fix dropdown with manual options showing SQL error
Fixed an issue where dropdowns using manual child option tags (no data attribute) would show an "Unknown table function WHERE" error.
✨ Multiplayer editor now in beta
Multiplayer editor is now available in beta. Turn it on from Project Settings to collaborate with your team in real-time.
🐛 Graceful redirect when page not found after branch switch or rename
Fixed an issue where switching branches or renaming a page could result in a 404 when the current page doesn't exist on the target branch or at the old URL.
🐛 Fix dimension link navigation resulting in 404
Fixed an issue where clicking dimension links in tables would result in a 404 when navigating between pages, because the URL wasn't being resolved relative to the current project context.
🐛 Automatic chart padding for data labels
Charts now automatically add top padding when data labels are positioned above bars or points, preventing labels from being cut off at the top of the chart.
✨ External GitHub branch support and PR preview links
Projects connected to GitHub now show all repository branches in the branch switcher, not just branches created through Evidence Studio. When a pull request is opened or reopened, Evidence automatically posts a preview link comment on the PR.
🚀 SQL Console Table Grouping
Tables in the SQL console sidebar are now organized by schema/connection in a collapsible tree structure for easier navigation.
✨ Customizable Total Label for Tables
Tables now support a total_label attribute that allows customizing the text displayed in total/subtotal rows and columns, useful when using non-sum aggregations like avg, min, max, or count.
✨ Editor Sidebar
Enhanced editor sidebar, with diff viewer, search, table schema, and page filters.
🚀 Pivot Table Performance
Significant performance improvements for pivoted tables and increased row/column limits.
🐛 Fix dropdowns closing modals
Fixed an issue where clicking on dropdown menus inside a modal would cause the modal to close unexpectedly.
🐛 Stable AI Edit Previews
Prevent AI edits from causing full refresh
✨ Gradient fill for area charts
Area charts now support a gradient fill option that fades from the series color at the top to transparent at the bottom. Enable it with area_options={ gradient=true }.
✨ Clock Component
Display a live clock with running seconds and the current date using {% clock /%}. Supports 12h/24h format, monospace font variant, and left/right alignment.
✨ Branching
Work on multiple versions of your reports at the same time. Create and switch between branches, commit your work, and publish when you're ready.
✨ Heat Grid
Visualize metrics across categories with color-coded cells based on configurable thresholds. Cells are colored red, yellow, or green depending on where values fall relative to your thresholds, with an optional lower_is_better mode.
🚀 Model Engine and Primary Key Settings
The model edit page now includes configurable refresh schedules, table engine selection, and primary key configuration with helpful tooltips explaining each engine type, data locations, and nullable primary key handling.
✨ Progress Bars
Display progress toward goals with horizontal bars showing numerator/denominator ratios. Supports threshold-based coloring and custom color gradients.
✨ PDF Export Options
PDF export now supports custom orientation, margins, and card styling options.
✨ Datadog Connector
Connect your Datadog monitoring and analytics data to Evidence. Sync metrics, dashboards, monitors, hosts, and usage data from your Datadog account.
✨ Custom Title for Table Filter
Customize the Table Filter button text using the new title attribute.
✨ Table Repeat Dimension Values
Tables and dimensions now support a repeat_values attribute that forces dimension values to display on every row instead of being collapsed.
🚀 Improved code editor font
The code editor now uses Geist Mono, a modern monospace font designed for better readability and aesthetics.
✨ Custom AI Instructions
Configure custom instructions for the AI assistant in Settings > AI Agent.
✨ RDS PostgreSQL Connector
Connect your RDS PostgreSQL database to Evidence and sync your data automatically.
🚀 PostgreSQL TLS Requirement Documentation
Added documentation note for PostgreSQL and Azure PostgreSQL connectors specifying that TLS/SSL must be enabled on the database for secure connections.
✨ Collapsible Tables
Tables with subtotals can now be collapsed. Click subtotal rows to expand or collapse their children.
🐛 Fixed row link chevron styling for total and subtotal rows
Fixed chevron column styling in tables with row links to match total/subtotal row backgrounds.
✨ Total and Subtotal Position
Control where totals and subtotals appear with total_position and subtotal_position. Set to "top" or "bottom".
🚀 Table Filter Initial Values and Required Selections
Table filters now support initial_values to set default filter values when the page loads, and require_selection to prevent specific filters from being cleared.
🚀 Automatic Unit Formatting for Numbers
BigValue, Value, and Table components now auto-format large numbers with compact units (e.g., $1.2M instead of $1,234,567)
✨ Text Size Option for Big Value
The big_value component now supports a text_size attribute with predefined options (sm, base, lg, xl, 2xl, 3xl, 4xl, 5xl) for reliable text sizing.
🚀 Clear Table Sorting with Third Click
Click a column header a third time to clear sorting and return to the original order.
✨ Azure SQL Database Connector
Sync data from Azure SQL Database directly into Evidence.
✨ Logo Attribute for Table Dimensions
Display company logos in table dimension columns. The new logo attribute accepts a column containing domains to look up logos, and logo_options lets you customize size, grayscale mode, and label visibility.
✨ PDF Export for Viewers
Viewers can now download published pages as PDFs using the page menu or the keyboard shortcut (Cmd/Ctrl+P).
✨ Twilio Connector
Connect your Twilio data to Evidence. Sync calls, messages, accounts, and usage records.
🚀 BigQuery 1 Minute Incremental Syncs
BigQuery data connector now supports 1 minute incremental syncs for faster data freshness.
✨ Column Groups in Tables
Group related columns under shared headers using the new column_group attribute on measures and dimensions. Useful for organizing metrics by category, such as revenue metrics vs volume metrics.
🐛 Dark Mode Empty Circle Markers Fix
Fixed emptyCircle markers in charts to properly use the background color in dark mode instead of white.
✨ Databricks Data Connector
Sync data from Databricks directly into Evidence.
✨ Google Ads & Facebook Ads Connectors
Connect your Google Ads and Facebook Ads data to Evidence. Sync campaigns, ad groups, ads, and performance metrics to build marketing analytics dashboards.
🐛 Horizontal Bar Chart Sorting Fix
Horizontal bar charts now default to sorting by value descending, showing the largest values at the top instead of the bottom.
✨ Logo Component
New logo component to display company logos using logo.dev. Supports light/dark theme switching, configurable sizes, and grayscale mode.
✨ MySQL Data Connector
Sync data from MySQL directly into Evidence.
✨ PostHog Connector
Connect your PostHog product analytics data to Evidence. Sync events, persons, cohorts, feature flags, insights, and more from your PostHog account.
🚀 Snowflake 1 Minute Incremental Syncs
Snowflake data connector now supports 1 minute incremental syncs for faster data freshness.
✨ SQL Server Data Connector
Sync data from SQL Server directly into Evidence.
✨ Dimension Grid
New dimension_grid component for interactive multi-dimensional filtering.
🐛 Fix for frozen columns row shading
Frozen columns in tables now display the correct alternating row shading when row_shading=true is enabled. Previously, frozen columns would not show the shading because they had a solid background that overrode the row-level shading.
✨ Series colors for pie charts
Map specific category values to specific colors in pie charts using the series_colors option in chart_options. This ensures consistent, meaningful coloring regardless of which categories are present in the data.
🐛 Export Data Retains Filters
Table exports now correctly include filtered and sorted data, matching the visual display on screen.
🐛 PDF Export Now Retains Filters
When downloading a PDF, any selected filter values are now preserved in the exported PDF.
🚀 Adjusted Developer Role Privileges
Previously, developers and admins could manage users, groups and customers. This is now restricted to Admins only.
✨ Motherduck Connector Schema Support
You can now use schema with the Motherduck connector
🐛 Dropdown Component Now Supports 'where' Attribute
The dropdown component now supports the where attribute for filtering dropdown options with custom SQL WHERE conditions, bringing it to feature parity with other filter components like button_group and input_tabs.
🐛 Horizontal Bar Chart X-Axis Formatting
Fixed issue where the x_fmt parameter was not being applied to axis labels in horizontal bar charts. Formatting now works consistently for both axis labels and tooltips.
✨ Add your organization logo
Add your organization's logo in settings
✨ Github Data Connector
Sync data about your Github repos directly into Evidence.
✨ Deltalake (S3) Connector
Connect using your S3 with Delta Lake format your data remains at rest in your S3 bucket.
✨ Smooth Line Option
New smooth attribute inside line options lets you create a line with rounded corners
✨ Chart `height` attribute
Control the height of your charts using the height attribute
✨ Customer Groups
Create customers, and invite customer users to view reports that you choose.
✨ Groups
Add team members to groups to simplify permissions on projects and pages
🚀 Improved Excel Export
Excel exports now use component titles to create tab names, and exclude logic and filter components
✨ User Variables
Access the user's name, email and organization using variables in markdown.
✨ Themes
Set color themes at the organization, project, and page level - including background, chart, and card colors
Access theme settings in page settings, project settings, or org settings. You can override theme colors at each level (e.g., project can override org theme, page overrides project and org themes)
🚀 Improved Translation Validation Errors
Incorrect structures for translation maps will provide useful errors
🚀 Translations Editor
Modify translations with a fully featured text editor
🚀 Improved bucket docs
Adds more information about using bucket connectors
✨ Translation Management
Define variables with different translations for multi-language apps.
✨ Standalone Horizontal Bar Chart Component
New dedicated horizontal_bar_chart component makes it easier to create horizontal bar charts
We recommend migrating any existing horizontal bar charts to this new component
✨ Display control for total and subtotals in table
Choose which totals or subtotals to display in your table
✨ Dark Mode Chart Colors
Configure colors for both light and dark mode in your project and org settings
✨ Input tabs full_width and align attributes
Input tabs now allows the same styling configuration as the Tabs component, including full width and right align
✨ Conditional Formatting
Apply conditional logic to choose colors for visualizations
✨ Series Color Mapping
Option to select specific colors for series in your charts
🚀 Assign access to pending users
You can now grant project and page access to users who have not yet accepted invitations to join the Evidence organization.
🚀 Control order of series in combo chart
Combo chart now displays your series in the order you define them in your markdown
✨ Draft Diff Viewer
Review your changes using the diff viewer before you publish
🚀 Icons in Autocomplete
Find components more easily in component autocomplete menus using the mini-icons
🚀 Smoother chart loads on input changes
Improved chart loading behaviour and animation when changing inputs on your page
🐛 Fix for table pivoting issues
Fixed issue where tables were not pivoting in certain situations
✨ `fmt` property for input options
Use dynamic formats in viz components when creating an input to select a metric - e.g., show 'usd' for sales or 'pct1' for growth
✨ Variable `label` property
You can now reference the label property of a variable - useful when adding labels to viz components based on input selection
🐛 Fix for custom date range end dates
Fixes issue where custom end date defined in SQL was not taking effect
🐛 Fix for scale_column
Fixes issue where scale_column in the measure component was not properly assigning colors
🚀 Improved handling of date grains in charts
Automatically handles formatting and positioning of labels on x-axis for date grains including month of year and day of week
✨ Table of Contents
A generated table of contents from the headings in your report. Enable from page settings.
✨ Top padding option for charts
Top padding override option to allow more room for data labels - helps in cases where labels are cut off
✨ x_sort attribute
Choose a specific sort order for your x-axis, including specifying explicit strings
🐛 Histogram support for inline queries
Fix for Histogram issue where it could not accept inline queries
🚀 Toggle `invert` attribute
Invert attribute added to make toggled state = false instead of true
✨ Slider Input
New slider input component to filter on numeric values
✨ Query timing metadata
Queries in the SQL Console, and the pop out component inspector at the bottom of the editor now display how long the query took to run in the Evidence query engine
✨ Zoomable charts
Adds a new "zoom" chart option to enable zooming into the data
✨ Toggle input
New toggle input component which sets a boolean variable you can use in queries and components
✨ Button group and input tabs
New input components as visual alternatives to dropdowns: button_group and input_tabs
Note that this update also deprecates the dropdown_option component in favour of the more general option component. The option component can be used inside dropdown, button_group, or input_tabs
🚀 Info Links
Add links to info text popovers using info_link and info_link_title atttributes.
🚀 User Management Performance
Adding and removing users from reports is now faster
🐛 Fix for custom pivot formatting
Formatting for pivots now works as expected for custom date formats - e.g., fmt="yyyy-mm"
✨ Select all in dropdown
New button to select all options in a dropdown
✨ Text input component
Input component accepting free text input
✨ Feature Requests
Submit and track feature requests directly in your settings menu
From Settings > Feature Requests, you can create requests or bug reports, and set their priority. This integrates directly with the Evidence team's roadmap, with status changes synced into your view
✨ Stacked bar series in combo charts
Bar series in combo charts can now be stacked using the new stack_id attribute. Bars with the same stack_id will be stacked together, allowing for flexible combinations of stacked and grouped bars.
✨ Max label length for x-axis labels
Truncate labels after a certain number of characters using the max_label_length attribute inside x_axis_options
✨ Rotate x-axis labels
Use the new label_rotate option in x_axis_options to rotate the labels on your x-axis
🚀 Sidebar Icons and Sorting
Add custom icons and adjust the sort order of projects and pages in the sidebar
✨ Jump to Code
In the editor preview, Cmd/Ctrl + click on a component to jump to the code for that component
You can jump to code by Cmd/Ctrl + clicking a component in the preview pane, or you can jump to the component preview by Cmd/Ctrl + clicking the code in your editor
🐛 Fixes for table pivoting
Fixed issue where columns were not properly pivoting in tables
🚀 Drag and drop image support
Drag, or paste images into the editor to add them to your page.
✨ Models (Beta)
Join sources together and materialize the results to improve performance
Models can be used to execute joins across sources, centralize type casting, and create clean tables to use when building reports. Models are run and materialized every 8 hours
✨ Set custom end date for date ranges
Use a dynamic end date for date ranges based on your data or a standard offset
By default, date ranges end as of today's date, but this setting allows you to choose a date based on a standard offset (e.g., 1 day ago) or by passing a sql query to calculate the date (e.g., select max(date) from my_table)
✨ Bar chart opacity setting
Customize the opacity of bars in bar chart and combo chart components
🚀 Project Sections
Organize projects into sections for easier navigation during development
Sections will not impact how projects are arranged in the app published to Viewer users
🐛 Version control overflow
Fixes a bug where if you changed lots of files it overflowed the Version History panel
✨ Option to choose first day of week
In project settings, you can now choose whether weeks start on Sunday or Monday
🚀 Better SQL Autocomplete Suggestions
Improved SQL suggestions for columns, and tables after joins
🚀 Variable name shortcuts + use in markdown
Variables no longer require specifying a property - simply use the name, like {{ my_var }}
You can also use variables directly in markdown, including in headers and lists. You still have access to variable properties and can reference them like this: {{ my_var.selected }}
🚀 Performance improvement for table comparisons
Tables containing comparisons should now load much faster
🚀 More source sync info
View the number of rows in a source, and the amount of time it took for the source sync from the Sources page.
✨ Use variables inside components
You can now reference filter variables directly in component attributes
Supports the following attributes: title, subtitle, info, where, date_grain, date_range.range, comparison.compare_vs
✨ Icon support for dropdowns
Include an icon in your dropdown using the icon option
✨ Selector components for date grain and comparisons
Special components for selecting date grain and comparisons
🚀 Dynamic date ranges
Input arbitrary ranges into the date_range attribute in components, like "last 14 weeks" or "2023-01-14 to 2025-11-30"
🐛 Filter scroll fix
Fixed a bug where selecting a filter caused the page to jump back to the top.
✨ Select preset ranges in range calendar
Choose which preset ranges you want your users to see when clicking the range calendar component
🐛 SQL comments in inline queries
You can now add comments to inline queries without breaking them
✨ Option to turn legend off in charts
You can now turn off chart legends with legend=false
🚀 Conditional component render instantly
When previewing or viewing published pages, the initial state of any if/else_if/else conditions will be pre-calculated before the page loads.
🚀 Better label spacing on x-axis
Improvement to the alignment and spacing of labels on the x-axis
🐛 Fix target comparison totals in table
Fixed an issue where target comparisons were hidden in total rows
🐛 Improved date behaviour
Previously, database columns that returned Date type would return DateTime precision, which is confusing in the UI. Now dates will return no additional time component
Values that are Date type will no longer display time component in Tables, Values, and in various input components. Eg a Date previously formatted 2025-09-22T14:05:23Z would now be returned as 2025-09-22.
🐛 Fix Explore table resize and scroll
Fixes an issue where tables on the Explore page would resize and cut off content
🚀 Comparison Tooltip Improvements
Fixed issue with percentage calculations for negative values. Added support for custom formats.
✨ Make Feature Requests in AI Chat
Now you can ask the Evidence chat to submit a feature request to the Evidence team
🐛 Fixed unexpected date offsets
In certain timezones, dates were being transposed by a small number of seconds in Chrome and Safari. This could cause date formats to render incorrectly, for example showing the day before the expected date.
🚀 RLS Enhancements
Added variable support for IN/NOT IN operators, 'Apply All' rule option, new panel interface, and per-user variable editing capabilities.
🚀 Improved Inline SQL Editor
See available variables, inline queries, and a preview of compiled SQL when editing queries in the inline SQL editor
🚀 Improved international character support in SQL queries
You can now include Japanese, Chinese, emoji, and other non-Latin characters directly in SQL passed to components
✨ Page Loading (table)
Tables now load instantly on preview and published pages
This is the first change in a journey to fully enable Server-Side Rendering in Evidence pages. The table is the first component to support this behavior, which enables the following features:- Tables load instantly on preview and published pages (no more loading spinner when the page loads)- Chat can now debug SQL query errors in Table componentsThis change lays the groundwork for other components to opt into this new Server-Side Rendering system.
✨ Add optional dark_url prop to image component
Image components can now display different images in light and dark modes using the new dark_url prop.
✨ Themes
Define a custom color palette to apply to all charts in a project
🚀 Improved Default Funnel Chart Styling
Funnel Charts are more intuitive to read, with a squared off style
🐛 Fix Comparison Issues
Fixed bug for target comparisons which resulted in undefined error
✨ Inline SQL Editor
A new SQL editor for running and modifying inline queries on your page
✨ Icon Component
Add icons inline into markdown, with configuration for color and size
✨ Row Level Security
Add rules to restrict which rows of are visible to users from sources
Add RLS rules to data source columns, and add user variables to determine which values in these columns allow access.
🚀 Custom Color Palettes
All charts now support custom color palettes via the "chart_options" attribute
Pass an array of hex codes to the color_palette option e.g. color_palette=["#ff0000","#00ff00"]
🐛 Fix Editor Overwriting Bug
Fixed bug where switching pages and then hitting Ctrl+Z would overwrite your page with the content of the previous page.
🚀 Sidebar Hover Text
Adds hover text to sidebar items that are truncated due to length
🐛 Table Filter Option Overflow
Fixed a bug where long string values in the table filter overlapped each other
🚀 Charting Library Upgrade
Upgraded to ECharts 6, fixed some issues where labels got cut off
🐛 Expose SQL Errors
Exposes some SQL errors that were not being shown
🐛 Fix High Precision Decimals in Postgres
Resolves issue where tables containing decimal columns with more that 9 digits of precision were failing to publish
🚀 Editor Loading Speed
We've significantly improved the load speeds when switching between pages in the editor.
✨ Link Button
Use the link button to link to another page in Evidence or an external location
✨ Print Format Components
Add page breaks, print groups, and hide elements from PDF downloads
✨ PDF Download
Download your page as a PDF
🐛 If / Else Behaviour Fix
Fixed some issues with the if /else components which sometimes caused them to not display information when they should
✨ Accordian components
Organize content into collapsible accordian sections
✨ Axis Label Interval Control
More control over the intervals between labels on charts
🐛 Long Page Name Overflow
Long page and directory names now truncate in the sidebar to prevent the action button being hidden
✨ Range Calendar Input
New range calendar component for date filtering.
✨ Tabs
New Tabs, and Tab components for tabbed layouts.
✨ Variables
Define variables in frontmatter and use them in markdown, SQL and components
✨ Column-level control for tables
Control formatting for each column in a table
✨ Conditional formatting in tables
Use color scale to conditionallyformat values in a table
✨ Image support in tables
Add images to columns in a table
🚀 Improved Explore page
Supports more date grain options and improved share experience
🚀 Improved pivot table
Table now supports more complex pivot table scenarios
✨ Link support in tables
Add links to columns in a table, or set up row links
🚀 New date grain options
Inclues special grains like 'day of week' and 'month of year', with built-in labels
✨ New table visualizations
Add bar charts, sparklines, and deltas to your columns
✨ Period over period comparisons
Automatically compare values vs. prior year, prior period, or target. Available in table and big value.
🐛 Delete Connections Fix
Fixed a bug which only allowed you to delete the most recently added connection
✨ Hubspot Connector
Import data from Hubspot directly into Evidence
✨ Modals
New modal component that can contain content in a dialogue or drawer.
✨ Motherduck Connector
Add data sources from your Motherduck DB
✨ Page linking
Link to other Evidence pages using markdown links []() with Intellisense support
🐛 Respect columns option in table filter
Fix bug which ignored columns selection in table filter
🐛 Table filter responsiveness
Fix bug which prevented table filter inputs from taking effect immediately after adding the table filter to the page.
✨ Filter interpolation
Reference filter values inside of inlined queries, and view the state of your filters in the new filters pane of the devtools sidebar.
🚀 Agent Keyboard Shortcut
Ctrl+Alt+I (Mac: Cmd+Shift+I) to open a chat with the Evidence agent from anywhere in the Editor
✨ If, Else If, Else
Use conditional blocks to choose what to render based on data.
✨ Reference Components
Use reference_line, reference_area, and reference_point to add reference annotations to your charts
🚀 Postgres Schema Support
Use tables in any Postgres schema (not just the default "public" schema)
✨ Import Views as Sources
All supported DBs now allow importing views as sources
🚀 Public Documentation
We've published full documentation at https://docs.evidence.studio
✨ Card Option for Row and Stack Components
Added card option to row and stack components to display their contents as a single card.
✨ Data Point Labels
Label each data point in your chart with the data_labels option
✨ Downloadable Data
Right click any chart to download its data, or use the kebab menu in the top left of the published page to download a whole page's data.
✨ Chart, Axis, and Series Options
New chart_options, x_axis_options, y_axis_options, y2_axis_options, and series options for customizing your charts.
✨ Partials
Define sections of markdown for re-use across pages.
✨ Polar Chart
Added a new polar chart component.
✨ Table Search
Search bar for the Table component.
🐛 Inline Query Date Validation
Fixes issue creating visualizations using inline queries with dates
✨ Histogram
Added a new histogram chart component.
✨ Markdown Comments
Adds support for mardown comments with <!-- -->. Mac: Cmd + / Windows: Ctrl + /
✨ Calendar Heatmap
Added a new calendar heatmap chart.
✨ Combo Chart
Added a combo_chart component that allows you to combine area, bar, bubble, line, and scatter series on one chart.
✨ Accept and Reject Changes Panel in Diff View
Added accept and reject buttons to the top of the diff view.
🚀 Connection Documentation
Added generaldocumentation for data connections.
🐛 Fix to give AI agent knowledge of inline queries
Fixed the issue where the AI agent did not know about inline queries on your page.
🐛 Fix to Maintain Chart Height
Fixed the issue where the chart height was shrinking when title and legend were added.
✨ Heatmap Chart
Added a new heatmap chart component.
🚀 Link to All Docs in Sidepane
Added a link to all docs in the docs tab of the editor.
✨ Custom X and Y Chart Titles
Added the ability to customize x_title and y_title for charts.
✨ Funnel Chart
Added a new funnel chart component.
✨ Sankey Chart
Added a new sankey chart component.
✨ Changelog
We're shipping features and fixes very quickly. Keep an eye out here for updates.
🐛 Fix Slash Commands When Inside Parent Component
Fixed the issue where slash commands were not working when inside a parent component.
🚀 Helpful Warning for Aggregating Columns
Added a warning to consider aggregating a column for a chart.
✨ Open Chat Automatically for Fix in Chat
When the fix in chat button is pressed, the chat window will open automatically.
✨ Pie and Donut Charts
Added a new pie chart component.
🚀 Collapse the Sidebar
Added the ability to collapse and expand the sidebar on all screen sizes.
🚀 Faster Sources Page
Significantly improved loading performance on the sources page.
🚀 Fix in Chat Now Available on All Error Messages
Error messages throughout the platform now include the fix in chat feature.
🚀 More Readable SQL Errors
SQL error messages have been redesigned to be clearer and more actionable.
🐛 Row and Stack Components
Fixed issues with Row and Stack components rendering and layout behavior.
✨ European Number Formatting
Support for European number format using comma as the decimal separator (e.g., 1.234,56 instead of 1,234.56).
✨ Azure Postgres Connector
Connect your Azure Database for PostgreSQL to Evidence and sync your data automatically.
✨ SQL Files
Create reusable SQL queries as standalone files. SQL files can be referenced from components using data="path/to/query" and from inline queries using {{path/to/query}} syntax.
Docs.Json (`docs/docs.json`)
{
"$schema": "https://mintlify.com/docs.json",
"theme": "maple",
"thumbnails": {
"fonts": {
"family": "Geist"
}
},
"icons": {
"library": "lucide"
},
"name": "Evidence Docs",
"colors": {
"primary": "#2c5282",
"light": "#2c5282",
"dark": "#15803D"
},
"integrations": {
"posthog": {
"apiKey": "phc_5gRFl1Rp6a01CZM9CX3qDvFwyxp7YzdWvdGAgi8a1fq"
}
},
"favicon": "/favicon.svg",
"redirects": [
{
"source": "/features/embedded",
"destination": "/features/embedded-analytics"
},
{
"source": "/features/code-managed-access",
"destination": "/features/page-level-access-control"
},
{
"source": "/features/page-frontmatter",
"destination": "/features/page-settings"
},
{
"source": "/features/access-control",
"destination": "/features/row-level-security"
},
{
"source": "/features/access-rules",
"destination": "/features/row-level-security"
},
{
"source": "/features/project-configuration",
"destination": "/features/project-settings"
},
{
"source": "/features/mcp-server",
"destination": "/mcp/docs"
},
{
"source": "/features/evidence-mcp",
"destination": "/mcp/studio"
},
{
"source": "/install-evidence",
"destination": "/quickstart"
},
{
"source": "/getting-started/install-evidence",
"destination": "/quickstart"
},
{
"source": "/build-your-first-app",
"destination": "/quickstart"
},
{
"source": "/core-concepts/queries",
"destination": "/evidence-warehouse"
},
{
"source": "/core-concepts/formatting",
"destination": "/core-concepts/value-formatting"
},
{
"source": "/core-concepts/filters",
"destination": "/components/filter_bar"
},
{
"source": "/core-concepts/themes",
"destination": "/features/themes"
},
{
"source": "/core-concepts/templated-pages",
"destination": "/features/page-settings"
},
{
"source": "/core-concepts/exports",
"destination": "/components/download"
},
{
"source": "/core-concepts/syntax",
"destination": "/core-concepts/markdown"
},
{
"source": "/core-concepts/pages",
"destination": "/features/page-settings"
},
{
"source": "/core-concepts/query-functions",
"destination": "/core-concepts/models"
},
{
"source": "/core-concepts/if-else",
"destination": "/components/if"
},
{
"source": "/core-concepts/loops",
"destination": "/components/repeat"
},
{
"source": "/core-concepts/data-sources/duckdb",
"destination": "/evidence-warehouse"
},
{
"source": "/core-concepts/data-sources/bigquery",
"destination": "/direct-connectors/bigquery"
},
{
"source": "/core-concepts/data-sources/postgres",
"destination": "/direct-connectors/postgres"
},
{
"source": "/core-concepts/data-sources/javascript",
"destination": "/evidence-warehouse"
},
{
"source": "/core-concepts/data-sources/csv",
"destination": "/evidence-warehouse"
},
{
"source": "/core-concepts/data-sources/snowflake",
"destination": "/direct-connectors/snowflake"
},
{
"source": "/core-concepts/data-sources/google-sheets",
"destination": "/evidence-warehouse"
},
{
"source": "/core-concepts/data-sources/databricks",
"destination": "/direct-connectors/databricks"
},
{
"source": "/core-concepts/data-sources/trino",
"destination": "/evidence-warehouse"
},
{
"source": "/core-concepts/data-sources/mssql",
"destination": "/evidence-warehouse"
},
{
"source": "/core-concepts/data-sources/motherduck",
"destination": "/direct-connectors/motherduck"
},
{
"source": "/components/charts/echarts-options",
"destination": "/components/custom_echart"
},
{
"source": "/components/charts/custom-echarts",
"destination": "/components/custom_echart"
},
{
"source": "/components/charts/bar-chart",
"destination": "/components/bar_chart"
},
{
"source": "/components/charts/line-chart",
"destination": "/components/line_chart"
},
{
"source": "/components/charts/sankey-diagram",
"destination": "/components/sankey_chart"
},
{
"source": "/components/charts/heatmap",
"destination": "/components/heatmap"
},
{
"source": "/components/charts/calendar-heatmap",
"destination": "/components/calendar_heatmap"
},
{
"source": "/components/charts/mixed-type-charts",
"destination": "/components/combo_chart"
},
{
"source": "/components/charts/funnel-chart",
"destination": "/components/funnel_chart"
},
{
"source": "/components/charts/annotations",
"destination": "/components/reference_line"
},
{
"source": "/components/charts/scatter-plot",
"destination": "/components/scatter_chart"
},
{
"source": "/components/charts/area-chart",
"destination": "/components/area_chart"
},
{
"source": "/components/charts/histogram",
"destination": "/components/histogram"
},
{
"source": "/components/charts/sparkline",
"destination": "/components/sparkline"
},
{
"source": "/components/charts/bubble-chart",
"destination": "/components/bubble_chart"
},
{
"source": "/components/data/data-table",
"destination": "/components/table"
},
{
"source": "/components/data/big-value",
"destination": "/components/big_value"
},
{
"source": "/components/data/value",
"destination": "/components/value"
},
{
"source": "/components/data/delta",
"destination": "/components/delta"
},
{
"source": "/components/inputs/dropdown",
"destination": "/components/dropdown"
},
{
"source": "/components/inputs/date-range",
"destination": "/components/range_calendar"
},
{
"source": "/components/inputs/date-input",
"destination": "/components/range_calendar"
},
{
"source": "/components/inputs/dimension-grid",
"destination": "/components/dimension_grid"
},
{
"source": "/components/inputs/button-group",
"destination": "/components/button_group"
},
{
"source": "/components/inputs/slider",
"destination": "/components/slider"
},
{
"source": "/components/inputs/text-input",
"destination": "/components/text_input"
},
{
"source": "/components/inputs/checkbox",
"destination": "/components/toggle"
},
{
"source": "/components/inputs",
"destination": "/components/dropdown"
},
{
"source": "/components/maps/area-map",
"destination": "/components/area_layer"
},
{
"source": "/components/maps/point-map",
"destination": "/components/point_layer"
},
{
"source": "/components/maps/bubble-map",
"destination": "/components/point_layer"
},
{
"source": "/components/maps/us-map",
"destination": "/components/map"
},
{
"source": "/components/maps/base-map",
"destination": "/components/map"
},
{
"source": "/components/ui/embed",
"destination": "/components/iframe"
},
{
"source": "/components/ui/link",
"destination": "/components/link"
},
{
"source": "/components/ui/grid",
"destination": "/components/row"
},
{
"source": "/components/ui/tabs",
"destination": "/components/tabs"
},
{
"source": "/components/ui/big-link",
"destination": "/components/link_button"
},
{
"source": "/components/ui/link-button",
"destination": "/components/link_button"
},
{
"source": "/components/ui/image",
"destination": "/components/image"
},
{
"source": "/components/ui/info",
"destination": "/components/info"
},
{
"source": "/components/ui/alert",
"destination": "/components/callout"
},
{
"source": "/components/ui/download-data",
"destination": "/components/download"
},
{
"source": "/components/ui/details",
"destination": "/components/details"
},
{
"source": "/components/ui/print-format-components",
"destination": "/components/print_group"
},
{
"source": "/components/ui/accordion",
"destination": "/components/accordion"
},
{
"source": "/components/ui/modal",
"destination": "/components/modal"
},
{
"source": "/components/ui/note",
"destination": "/components/note"
},
{
"source": "/components/custom/custom-component",
"destination": "/core-concepts/custom-components"
},
{
"source": "/components/custom/component-queries",
"destination": "/core-concepts/custom-components"
},
{
"source": "/components/all-components",
"destination": "/core-concepts/components"
},
{
"source": "/reference/layouts",
"destination": "/features/layouts"
},
{
"source": "/reference/markdown",
"destination": "/core-concepts/markdown"
},
{
"source": "/reference/cli",
"destination": "/cli/commands"
},
{
"source": "/deployment/overview",
"destination": "/publishing"
},
{
"source": "/deployment",
"destination": "/publishing"
},
{
"source": "/deployment/self-host",
"destination": "/self-host"
},
{
"source": "/deployment/self-host/vercel",
"destination": "/self-host/vercel"
},
{
"source": "/deployment/self-host/github-pages",
"destination": "/self-host"
},
{
"source": "/deployment/self-host/netlify",
"destination": "/self-host"
},
{
"source": "/deployment/self-host/gitlab-pages",
"destination": "/self-host"
},
{
"source": "/deployment/self-host/hugging-face-spaces",
"destination": "/self-host"
},
{
"source": "/deployment/self-host/azure-static-apps",
"destination": "/self-host"
},
{
"source": "/deployment/self-host/firebase",
"destination": "/self-host"
},
{
"source": "/deployment/self-host/aws-amplify",
"destination": "/self-host"
},
{
"source": "/deployment/configuration/rendering-modes",
"destination": "/features/project-settings"
},
{
"source": "/deployment/configuration/environments",
"destination": "/features/project-settings"
},
{
"source": "/deployment/configuration/base-paths",
"destination": "/self-host"
},
{
"source": "/guides/system-requirements",
"destination": "/self-host"
},
{
"source": "/guides/updating-your-app",
"destination": "/migration-guide"
},
{
"source": "/guides",
"destination": "/quickstart"
},
{
"source": "/plugins/source-plugins",
"destination": "/evidence-warehouse"
},
{
"source": "/plugins/component-plugins",
"destination": "/core-concepts/custom-components"
},
{
"source": "/plugins/create-component-plugin",
"destination": "/core-concepts/custom-components"
},
{
"source": "/plugins/create-source-plugin",
"destination": "/evidence-warehouse"
},
{
"source": "/plugins",
"destination": "/core-concepts/custom-components"
},
{
"source": "/components/charts/box-plot",
"destination": "/components/custom_echart"
},
{
"source": "/components/ui/last-refreshed",
"destination": "/components/html"
},
{
"source": "/deployment/cloud/evidence-cloud",
"destination": "/publishing"
},
{
"source": "/guides/troubleshooting",
"destination": "/"
},
{
"source": "/guides/best-practices",
"destination": "/"
},
{
"source": "/deployment/self-host/cloudflare-pages",
"destination": "/self-host"
},
{
"source": "/getting-started/what-is-evidence",
"destination": "/"
},
{
"source": "/markdown",
"destination": "/core-concepts/markdown"
},
{
"source": "/themes-and-layouts",
"destination": "/features/layouts"
},
{
"source": "/plugins/creating-a-plugin",
"destination": "/core-concepts/custom-components"
},
{
"source": "/guides/updating-your-project",
"destination": "/migration-guide"
},
{
"source": "/components/area-map",
"destination": "/components/area_layer"
},
{
"source": "/components/us-map",
"destination": "/components/map"
},
{
"source": "/components/bubble-map",
"destination": "/components/point_layer"
},
{
"source": "/components/point-map",
"destination": "/components/point_layer"
},
{
"source": "/components/base-map",
"destination": "/components/map"
},
{
"source": "/components/download-data",
"destination": "/components/download"
},
{
"source": "/components/last-refreshed",
"destination": "/components/html"
},
{
"source": "/components/alert",
"destination": "/components/callout"
},
{
"source": "/components/big-link",
"destination": "/components/link_button"
},
{
"source": "/components/link-button",
"destination": "/components/link_button"
},
{
"source": "/components/print-format-components",
"destination": "/components/print_group"
},
{
"source": "/components/grid",
"destination": "/components/row"
},
{
"source": "/components/bubble-chart",
"destination": "/components/bubble_chart"
},
{
"source": "/components/mixed-type-charts",
"destination": "/components/combo_chart"
},
{
"source": "/components/funnel-chart",
"destination": "/components/funnel_chart"
},
{
"source": "/components/bar-chart",
"destination": "/components/bar_chart"
},
{
"source": "/components/custom-echarts",
"destination": "/components/custom_echart"
},
{
"source": "/components/box-plot",
"destination": "/components/custom_echart"
},
{
"source": "/components/annotations",
"destination": "/components/reference_line"
},
{
"source": "/components/calendar-heatmap",
"destination": "/components/calendar_heatmap"
},
{
"source": "/components/area-chart",
"destination": "/components/area_chart"
},
{
"source": "/components/line-chart",
"destination": "/components/line_chart"
},
{
"source": "/components/sankey-diagram",
"destination": "/components/sankey_chart"
},
{
"source": "/components/echarts-options",
"destination": "/components/custom_echart"
},
{
"source": "/components/scatter-plot",
"destination": "/components/scatter_chart"
},
{
"source": "/components/custom-components",
"destination": "/core-concepts/custom-components"
},
{
"source": "/components/component-queries",
"destination": "/core-concepts/custom-components"
},
{
"source": "/components/big-value",
"destination": "/components/big_value"
},
{
"source": "/components/data-table",
"destination": "/components/table"
},
{
"source": "/components/dimension-grid",
"destination": "/components/dimension_grid"
},
{
"source": "/components/checkbox",
"destination": "/components/toggle"
},
{
"source": "/components/date-range",
"destination": "/components/range_calendar"
},
{
"source": "/components/text-input",
"destination": "/components/text_input"
},
{
"source": "/components/button-group",
"destination": "/components/button_group"
},
{
"source": "/deployment/github-pages",
"destination": "/self-host"
},
{
"source": "/deployment/aws-amplify",
"destination": "/self-host"
},
{
"source": "/deployment/azure-static-apps",
"destination": "/self-host"
},
{
"source": "/deployment/cloudflare-pages",
"destination": "/self-host"
},
{
"source": "/deployment/firebase",
"destination": "/self-host"
},
{
"source": "/deployment/gitlab-pages",
"destination": "/self-host"
},
{
"source": "/deployment/hugging-face-spaces",
"destination": "/self-host"
},
{
"source": "/deployment/netlify",
"destination": "/self-host"
},
{
"source": "/deployment/vercel",
"destination": "/self-host/vercel"
},
{
"source": "/deployment/rendering-modes",
"destination": "/features/project-settings"
},
{
"source": "/deployment/base-paths",
"destination": "/self-host"
},
{
"source": "/deployment/environments",
"destination": "/features/project-settings"
},
{
"source": "/core-concepts/data-sources",
"destination": "/evidence-warehouse"
},
{
"source": "/data-sources",
"destination": "/evidence-warehouse"
},
{
"source": "/warehouses/evidence-warehouse",
"destination": "/evidence-warehouse"
}
],
"navigation": {
"global": {
"anchors": [
{
"anchor": "Evidence Studio",
"href": "https://evidence.studio",
"icon": "app-window"
},
{
"anchor": "Community",
"href": "https://slack.evidence.dev",
"icon": "slack"
},
{
"anchor": "Legacy Docs",
"href": "https://legacy-docs.evidence.dev",
"icon": "book"
}
]
},
"tabs": [
{
"tab": "Docs",
"groups": [
{
"group": "Get Started",
"pages": [
"index",
"quickstart",
"migration-guide",
"editing",
"publishing"
]
},
{
"group": "Core Concepts",
"pages": [
"core-concepts/markdown",
"core-concepts/components",
"core-concepts/models",
"core-concepts/metrics",
"core-concepts/value-formatting",
"core-concepts/variables",
"core-concepts/partials",
"core-concepts/custom-components"
]
},
{
"group": "Features",
"pages": [
"features/version-control",
"features/project-settings",
"features/page-settings",
"features/themes",
"features/layouts",
"features/sql-files",
"features/page-level-access-control",
"features/row-level-security",
"features/evidence-agent",
"features/translations",
"features/embedded-analytics",
"features/scheduled-reports"
]
},
{
"group": "MCP",
"pages": [
"mcp/overview",
"mcp/studio",
"mcp/docs"
]
},
{
"group": "CLI",
"pages": [
"cli/index",
"cli/connections",
"cli/commands"
]
},
{
"group": "Self Host",
"pages": [
"self-host/index",
"self-host/authentication",
"self-host/vercel",
"self-host/render",
"self-host/fly",
"self-host/railway"
]
},
{
"group": "Direct Connectors",
"pages": [
"direct-connectors/snowflake",
"direct-connectors/fabric",
"direct-connectors/clickhouse",
"direct-connectors/bigquery",
"direct-connectors/databricks",
"direct-connectors/postgres",
"direct-connectors/cube",
"direct-connectors/motherduck"
]
},
{
"group": "Evidence Warehouse",
"pages": [
"evidence-warehouse",
{
"group": "Databases",
"expanded": false,
"icon": "database",
"pages": [
"data-sources/athena",
"data-sources/motherduck",
"data-sources/redshift"
]
},
{
"group": "Object Storage",
"expanded": false,
"icon": "hard-drive",
"pages": [
"data-sources/abs",
"data-sources/backblaze",
"data-sources/custom",
"data-sources/gcs",
"data-sources/r2",
"data-sources/s3"
]
},
{
"group": "Data Lakes",
"expanded": false,
"icon": "layers",
"pages": [
"data-sources/deltalake_s3",
"data-sources/iceberg"
]
},
{
"group": "Flat Files",
"expanded": false,
"icon": "file",
"pages": [
"data-sources/file"
]
}
]
},
{
"group": "Components",
"pages": [
{
"group": "Values",
"expanded": false,
"icon": "shapes",
"pages": [
"components/value",
"components/big_value",
"components/delta",
"components/heat_grid",
"components/progress_bars"
]
},
{
"group": "Tables",
"expanded": false,
"icon": "table-2",
"pages": [
"components/table",
"components/dimension",
"components/measure",
"components/pivot"
]
},
{
"group": "Charts",
"expanded": false,
"icon": "chart-column-increasing",
"pages": [
"components/area_chart",
"components/bar_chart",
"components/bubble_chart",
"components/calendar_heatmap",
"components/candlestick",
"components/chord_chart",
"components/combo_chart",
"components/custom_echart",
"components/funnel_chart",
"components/heatmap",
"components/histogram",
"components/horizontal_bar_chart",
"components/line_chart",
"components/pie_chart",
"components/polar_chart",
"components/radar_chart",
"components/sankey_chart",
"components/scatter_chart",
"components/treemap",
"components/waterfall_chart",
"components/area",
"components/bar",
"components/bubble",
"components/line",
"components/reference_area",
"components/reference_line",
"components/reference_point",
"components/scatter",
"components/sparkline"
]
},
{
"group": "Maps",
"expanded": false,
"icon": "map",
"pages": [
"components/custom_map",
"components/map",
"components/area_layer",
"components/heatmap_layer",
"components/point_layer"
]
},
{
"group": "Inputs",
"expanded": false,
"icon": "text-cursor-input",
"pages": [
"components/button_group",
"components/comparison_selector",
"components/benchmark_comparison",
"components/target_comparison",
"components/date_grain_selector",
"components/dropdown",
"components/filter_bar",
"components/input_tabs",
"components/option",
"components/range_calendar",
"components/slider",
"components/table_filter",
"components/text_input",
"components/toggle",
"components/dimension_grid",
"components/period_selector"
]
},
{
"group": "Custom",
"expanded": false,
"icon": "code-xml",
"pages": [
"components/html"
]
},
{
"group": "UI",
"expanded": false,
"icon": "layout-template",
"pages": [
"components/accordion",
"components/accordion_item",
"components/accordion_title",
"components/audio",
"components/callout",
"components/clock",
"components/commentary",
"components/details",
"components/download",
"components/fence",
"components/file",
"components/hidden_in_print",
"components/icon",
"components/iframe",
"components/image",
"components/info",
"components/line_break",
"components/link",
"components/link_button",
"components/logo",
"components/modal",
"components/note",
"components/only_in_print",
"components/page_break",
"components/partial",
"components/print_group",
"components/row",
"components/stack",
"components/tab",
"components/tabs"
]
},
{
"group": "Logic",
"expanded": false,
"icon": "trending-up-down",
"pages": [
"components/if",
"components/else_if",
"components/else",
"components/repeat"
]
}
]
}
]
},
{
"tab": "Changelog",
"groups": [
{
"group": "Changelog",
"pages": [
"changelog"
]
}
]
}
]
},
"logo": {
"light": "/logo/wordmark-light.png",
"dark": "/logo/wordmark-dark.png"
},
"footer": {
"socials": {
"x": "https://x.com/evidence_dev",
"github": "https://github.com/evidence-dev",
"linkedin": "https://linkedin.com/company/evidence-dev",
"slack": "https://slack.evidence.dev",
"bluesky": "https://bsky.app/profile/evidence.dev"
}
}
}
Editing (`docs/editing.mdx`)
title: 'Editing'
import StudioOnly from '/snippets/studio-only.mdx';
Code Editor
On the left hand side of your screen, you have a code editor. This is where you define your report in markdown.
The code editor is enriched for data development with:
- Slash commands: Type a
/to list components, accept suggestions withtab. - Autocompletion: For components and SQL fences, accept suggestions with
tab. - Syntax highlighting: For markdown, SQL, and components.
- Validation: Invalid code is underlined in red, hover to see errors, and click "Fix" for AI assistance.
Preview
As you modify the code in the editor, the preview will update in real time, including rerunning SQL queries and rerendering charts when you make changes.
Nothing is published until you click the "Publish" button. See Publishing for details.
Evidence Agent
The right sidebar contains a chat interface with the Evidence Agent.
- The agent is trained to generate valid Evidence markdown
- It knows the schema of any data sources you have added
The agent can generate reports, fix errors, and answer general questions about Evidence Studio.
Agent Capabilities
The agent can help you across most of what you do in Studio, including:
- Read and edit pages: Generate new reports or make targeted edits to existing markdown, SQL, and components.
- Run SQL queries: Execute SQL against your connected data sources to explore data and validate results before writing it into a report.
- Inspect your data: Read the schema and metadata of tables, sources, and models, and list available connections to see what data is available.
- Read existing visualizations: Pull the data behind a chart, table, or value already on the page to reason about and extend it.
- Update settings: Adjust project and page settings (theme, layout, table of contents, default date range, and more) and edit project config files like
theme.yamlandevidence.config.yaml. - Diagnose errors: Identify and fix invalid SQL, broken components, and other issues in your reports.
- Search the docs: Answer questions about Evidence Studio, components, and SQL using the documentation.
The Evidence Agent suggests changes in a "diff view", which you can accept, reject, or refine with another message.
Evidence Warehouse (`docs/evidence-warehouse.mdx`)
title: 'Evidence Warehouse'
sidebarTitle: 'Overview'
keywords:
- data warehouse
- managed warehouse
- data integration
- cloud warehouse
- direct connectors
- data lake
- etl data integration
- zero etl
import StudioOnly from '/snippets/studio-only.mdx';
The Evidence Warehouse is Evidence's built-in, fully managed cloud warehouse. You connect a data source once, Evidence extracts the data into the warehouse, and every report queries it from there — fast, with no infrastructure for you to run. You choose which region the data is stored in (see Storage Regions). When you use the managed warehouse, Evidence handles etl data integration for you — extracting, loading, and making data available for reporting on a schedule.
Connectors are how you get data into the Evidence Warehouse. You connect to your data source once, then use that data across all your reports. With this data integration workflow, your reports always query the same extracted data, regardless of which source it came from.
Evidence supports different types of connectors: Databases, Object Storage, Data Lakes, Flat Files, and Applications.
If you'd rather have Evidence query a warehouse you own directly — without extracting data into the Evidence Warehouse — use a native connector instead.
Databases
Database connectors connect to your existing data warehouse or database. Evidence extracts the data, and you can define a schedule for sync.
How it works:
- You provide connection credentials to your database
- Evidence runs queries against your database to extract data
- Data is cached in Evidence's query engine in your chosen region
- Reports query the cached data for fast performance
You choose where your extracted data is stored. See available regions.
Best for:
- Existing data warehouses (Snowflake, BigQuery, Redshift, etc.)
- Data that's already modeled and ready to report on
- Teams who want Evidence to manage the data pipeline
Supported databases:
- Snowflake
- BigQuery
- PostgreSQL
- RDS PostgreSQL
- Redshift
- MotherDuck
- Athena
- MySQL
- SQL Server
- Azure SQL Database
- Azure Postgres
- Databricks
We are continuously adding support for new databases. Request one here.
Direct Connectors
Prefer to keep your data in your own warehouse? Native connectors are the alternative to the Evidence Warehouse: instead of extracting data, Evidence runs queries live against a warehouse you own. Each request from a viewer issues queries against your warehouse and returns the rows directly. This is a zero etl approach — since the warehouse is queried directly, there's no extraction, loading, or sync pipeline to maintain.
How it works:
- You provide credentials with read access to your warehouse
- Evidence issues queries directly to your warehouse when viewers load reports
Key characteristics:
- Always live: viewers see the current state of your warehouse on every load — no sync schedule.
- Data stays in your warehouse: Evidence holds credentials but doesn't store data.
Best for:
- Use cases where data freshness is important
- Strict data residency requirements
- Teams that already maintain RLS policies in their warehouse and want Evidence to honour them
Native connectors live in the Warehouses section:
Object Storage
Object storage connectors read Parquet files directly from cloud storage buckets that you control. Evidence queries the data in place using ClickHouse's S3 table engine. In addition to the below named providers, any S3 compatible storage is supported (via the "custom S3" connector).
How it works:
- You store Parquet files in your own bucket (S3, GCS, Azure, etc.)
- You provide bucket credentials to Evidence
- Evidence queries the Parquet files directly — no data is copied
Key characteristics:
- Instant data availability: When you update a Parquet file in your bucket, the new data is immediately available in Evidence—no sync or publish required
- Schema changes require publish: Adding or removing columns requires clicking Publish or setting up a refresh schedule
- Data stays in your infrastructure: Your data never leaves your bucket, Evidence queries it in place
- S3-compatible: Any storage provider with an S3-compatible API works (AWS S3, GCS, Azure Blob, Cloudflare R2, MinIO, Backblaze B2, etc.)
Best for:
- Data residency or sovereignty requirements
- Teams who manage their own data pipelines and produce Parquet files
- High-frequency data updates where you control the source files
- Keeping data within your own infrastructure
Supported providers:
- AWS S3
- Google Cloud Storage
- Azure Blob Storage
- Cloudflare R2
- Backblaze B2
- Custom S3-compatible provider
Data Lakes
Data lake connectors connect to open table formats that provide ACID transactions, schema evolution, and time travel capabilities on top of object storage.
How it works:
- You provide credentials to your data lake catalog or storage
- Evidence reads the table metadata and data files
- Data is queried with full support for the table format's features
Best for:
- Teams using modern lakehouse architectures
- Large-scale data with complex partitioning
- Scenarios requiring time travel or schema evolution
Supported data lakes:
Applications
Application connectors sync data from SaaS applications and APIs. Evidence uses Fivetran to extract and normalize data from these sources.
How it works:
- You authenticate with the application (OAuth or API key)
- Evidence syncs data on a configurable schedule
- Data is normalized and stored in Evidence's query engine
Best for:
- Product analytics and operational reporting
- Combining SaaS data with your data warehouse
- Teams who want turnkey integrations without building pipelines
Supported applications:
We are continuously adding support for new applications. Request one here.
Flat Files
Upload flat files directly to Evidence for quick data analysis without setting up external connections.
How it works:
- Upload a file directly through the Evidence interface
- Evidence processes and stores the data
- Query the data immediately in your reports
Supported formats:
- CSV
- Parquet
- JSONL
- Excel
Best for:
- Quick prototyping and ad-hoc analysis
- Small datasets that don't require a database
- Getting started with Evidence before connecting external sources
See Flat Files for details.
Comparison
| Databases | Object Storage | Data Lakes | Flat Files | Applications | |
|---|---|---|---|---|---|
| Data location | Data is extracted | Stays in your bucket | Stays in your storage | Uploaded to Evidence | Data is extracted |
| Data updates | Scheduled sync | Instant (for row changes) | Scheduled sync | Re-upload file | Scheduled sync |
| Schema changes | Requires re-publish | Requires re-publish | Requires re-publish | Re-upload file | Requires re-publish |
| Data residency | Choose Evidence region | Your bucket location | Your storage location | Choose Evidence region | Choose Evidence region |
Storage Regions
Evidence supports the following storage regions for extracted data.
| Region | Location |
|---|---|
us-central1 |
Iowa, USA |
us-east1 |
South Carolina, USA |
us-east4 |
Virginia, USA |
us-east5 |
Ohio, USA |
us-west1 |
Oregon, USA |
us-west2 |
Los Angeles, USA |
us-west3 |
Salt Lake City, USA |
us-west4 |
Las Vegas, USA |
northamerica-northeast1 |
Montreal, Canada |
northamerica-northeast2 |
Toronto, Canada |
europe-west2 |
London, UK |
europe-west3 |
Frankfurt, Germany |
europe-west4 |
Eemshaven, Netherlands |
europe-west6 |
Zurich, Switzerland |
europe-west8 |
Milan, Italy |
europe-west9 |
Paris, France |
europe-west10 |
Berlin, Germany |
asia-south1 |
Mumbai, India |
asia-southeast1 |
Singapore |
asia-northeast1 |
Tokyo, Japan |
australia-southeast1 |
Sydney, Australia |
Create a connection
(For databases only)
- On the sources page, click New Connection
- Enter the connection details for your database. Note that you require one connection per schema at this time.
- Click Test to verify the connection is configured correctly.
- Click Save.
IP Whitelisting
If your database requires IP whitelisting, add the Evidence Studio IP addresses:
3.21.143.81
3.209.193.182
3.215.195.233
18.189.109.43
34.56.211.45
35.192.240.48
35.192.240.49
35.192.240.50
35.192.240.51
35.192.240.52
35.192.240.53
35.192.240.54
35.192.240.55
35.234.176.144
35.234.176.145
35.234.176.146
35.234.176.147
35.234.176.148
35.234.176.149
35.234.176.150
35.234.176.151
104.197.50.84
*.us-east4.gcp.proxy.prod.fivetran.com
*.us-central1.gcp.proxy.prod.fivetran.comImport a data source
- On the sources page, click New Source.
- Select the connection you just created.
- Choose from the tables in the connection.
- (Optional) Rename the source - this is used to reference the source in your reports.
- Click Save.
Import a file
- On the sources page, click Upload File.
- You can upload CSV, Parquet, and JSONL files.
- Your file will be automatically imported, and named using a
snake_caseversion of the filename.
Updating data
Manual refresh
- On the sources page, click the
...menu on the right of the source and select Publish.
Scheduled refresh
- In the sources page, click the
...menu on the right of the source and select Schedule. - Choose a refresh interval.
- Current refresh intervals are: 5 mins, 15 mins, 1 hour, 1 day, 1 week. Contact Evidence for custom intervals.
- Click Save.
Migration Guide (`docs/migration-guide.mdx`)
title: Migration Guide
Legacy Evidence is a static website generator: Markdown and DuckDB SQL which queries data you package up in a build step. Evidence Studio has a live connection to a database, and an updated set of components that can generate SQL.
How to migrate
The CLI has a evidence migrate command. It converts everything it can mechanically — component syntax, attribute names, input references, connections, source SQL - and prints a warning for items it cannot migrate.
The fastest path is to let a coding agent support your migration.
- Install the Evidence CLI (version ≥ 0.9.0)
- Add the migration skill to your project: Save the file below into your legacy project as
.claude/skills/evidence-migrate/SKILL.md, or the equivalent path for your assistant. - Ask your assistant to migrate the project: "Migrate this Evidence project to Studio syntax." The skill tells it which commands to run and how to work through the warnings.
To run it yourself: evidence migrate --dry-run to preview, evidence migrate to convert in place, then evidence validate for syntax and SQL errors and evidence dev to inspect the result.
The migration skill
/* Detailed source-code truncated for AI context efficiency. */What works differently
| Legacy Evidence | Evidence Studio | |
|---|---|---|
| Component syntax | Svelte: <LineChart data={orders} x=date y=sales /> |
Markdoc: {% line_chart data="orders" x="date" y="sum(sales)" /%} |
| Component data | Renders a query you wrote | Writes its own SQL — aggregations, date_grain, comparisons, filters, where |
| SQL | DuckDB, in-browser (WASM) | Server-side: ClickHouse, or your warehouse's dialect via a direct connector |
| Data prep | sources/*.sql queries run at build |
Sync data to Evidence on a schedule, or update your data warehouse |
| SQL files | queries/*.sql, interpolated with ${inputs.x.value} |
/queries/*.sql inlined verbatim — {{ }} variables do not resolve; move input-driven SQL into a SQL block on the page |
| Variables | ${inputs.category.value} |
{{category}}, or filters=["category"] — see Variables |
| Logic | Svelte/JS in pages: {#each}, {#if}, {@const} |
{% repeat %}, {% if %} |
| Builds | Build required for every data or content change | No build, Queries re-run on page load |
| Deployment | Configure hosting and a build pipeline | Publish in seconds, from Evidence Studio or linked GitHub repo |
| Scale | ~2M rows | Hundreds of millions of rows |
| Access control | Build it yourself | SSO, page permissions, row-level access rules, user groups |
| Self-service | Reports | Reports + Insights + AI Chat |
Not supported
| Legacy feature | Instead |
|---|---|
Templated pages ([param].md) |
One page driven by an input — a dropdown wired to every component via filters |
Source SQL queries (sources/) |
Sync the table, or a view in your database, then use Models |
| Svelte files and custom Svelte components | Custom components, or {% custom_echart %} / {% html %} |
| JS expressions and Svelte blocks in pages | {% repeat %}, {% if %}, or compute it in SQL |
| Box plots, Venn diagrams | No equivalent |
| Drill-through links on maps | No equivalent — maps support tooltips only |
Secondary y-axis (y2=) |
Rebuild as {% combo_chart %} with axis="y2" on the secondary series |
<ECharts> config objects |
Move the config into the {% custom_echart %} body |
| Horizontal 100% stacked bars | Regular stacked {% horizontal_bar_chart %} |
Individual attributes without an equivalent are dropped with a warning by evidence migrate; run it with --dry-run to see the full list for your project.
Getting help
Ask in the Slack community, or book a migration call.
Publishing (`docs/publishing.mdx`)
title: 'Publishing'
Evidence Studio
How to publish
There are three ways to publish:
- From the editor. Open the Publish menu in the top right of the editor and click Publish. The menu shows pending changes, any open pull request, and the current publish state.
- From local dev. Push to the project's published branch using git or the Evidence CLI. The push triggers a publish automatically.
- From GitHub. Merge a pull request into the published branch on GitHub.
You can use any combination of these — Studio, your local environment, and GitHub stay in sync through the project's repo.
Pull requests
When you open a pull request against the published branch, Evidence runs checks and posts a preview:
- Markdown validation check: Catches invalid syntax and broken components before merge.
access.yamlvalidation check: Validates page level access rules so a bad config can't take down viewer access.- Preview link: A comment on the PR linking to a deployed preview of the branch, so reviewers can see the report before merging.
The checks appear as status checks on the PR — you can configure branch protection in GitHub to block merges until they pass.
Publish history and rollback
Every project has a publish history page that lists every publish attempt (publishes, CLI/local pushes, and PR merges), with who triggered it, the commit, the result, and a detail view for failures.
From the history page you can:
- Roll back to any previous successful publish.
- Retry a failed publish.
Reliable publishes
- Failed publishes never affect viewers. If a publish fails, the previous successful version stays live. Viewers don't see broken reports while you fix the issue.
- Email alerts on failure. Org admins get an email when a publish fails.
- Project banner on failure. When the latest publish failed, every project page in Studio shows a banner linking to the failure in history.
Connecting GitHub
By default, every project is backed by an Evidence-managed git repo — you don't need to do anything to get the publish flow above. If you want the repo to live in your own GitHub organization so you can edit it locally, review changes through your existing PR process, or hook it into your own CI, connect the Evidence GitHub App:
- Install the Evidence GitHub App. From Organization Settings, connect Evidence to your GitHub account or organization. You can grant access to all repos or just the ones you choose, and update that selection later.
- Link a repo to a project. Open the project, then Project Settings → Version Control, and select Connect to GitHub repository. Pick the repo, the branch to use as the published branch, and the project root path.
- First publish. Click Publish in the editor to push the current state to GitHub. From then on, all three publish flows (editor, local dev, GitHub PR merge) work against your repo.
See Version Control for branching strategies and the list of files that are version controlled.
GitHub Enterprise Cloud
Enterprise organizations can connect a GitHub Enterprise Cloud account hosted on a dedicated *.ghe.com domain:
- In Organization Settings, select Connect GitHub Enterprise.
- Enter the enterprise hostname (for example,
arkaan.ghe.com) and the GitHub organization that owns your repositories. - Continue to GitHub to create and install the private, organization-owned Evidence Studio GitHub App.
- Return to a project's Version Control settings and select one of the repositories available to the App.
Evidence supports GitHub Enterprise Cloud with data residency on *.ghe.com. Self-hosted GitHub Enterprise Server domains are not supported. An Evidence organization can connect to either GitHub.com or one GHE.com host at a time. Disconnect all projects from the current host before replacing the organization connection.
Sharing access
Once a report is published, control who can view it with access.yaml. See Page Level Access Control for the full schema and patterns for sharing with teammates, groups, and external customers.
Roles are managed from the Team Settings page:
- Viewer: Can view published reports they've been granted access to.
- Org Viewer: Can view all published reports in the org.
- Developer: Can edit reports and configure data sources.
- Admin: Developer permissions plus organization settings.
Self Host
Alternatively, run your project as a self-hosted site — no Evidence Studio account required. See Self Host for the Docker image, project setup, and platform guides for Vercel, Render, Fly.io, and Railway, plus a feature comparison with Evidence Studio.
Cli Commands (`docs/cli/commands.mdx`)
title: 'Commands'
Development
`evidence init`
Scaffold a new Evidence project.
evidence init my-project
evidence init my-project --warehouse snowflake| Option | Description |
|---|---|
--warehouse |
Scaffold a connection.yaml for a direct warehouse connection: snowflake or bigquery. See Connecting a Warehouse. |
Without --warehouse, the project uses the managed Evidence Warehouse. With it, the CLI writes a connection.yaml (gitignored) so queries run directly against your own warehouse.
`evidence dev`
Start the local development server.
evidence dev
evidence dev --port 8080| Option | Description |
|---|---|
--port, -p |
Port to run on (default: 3000) |
Deploy
`evidence launch`
Connect this project to Evidence Studio and GitHub, then deploy by pushing to your repo. Interactive by default — it walks you through installing the GitHub App and creating (or linking) the Studio project.
evidence launch
evidence launch --name "My Project" --upload-credentials| Option | Description |
|---|---|
--name |
Name for the Studio project to create. |
--branch |
Branch to publish from (default: auto-detected). |
--root-directory |
Project root within the repo (default: auto-detected). |
--upload-credentials |
Upload warehouse credentials from connection.yaml to your org. Prompted for interactively; required to opt in when running non-interactively. |
evidence launch recognizes GitHub.com and GitHub Enterprise Cloud (*.ghe.com) remotes over HTTPS and SSH. If the detected GHE.com host is not connected yet, the CLI opens Organization Settings to guide an Evidence admin through creating and installing the private Enterprise GitHub App. GHE.com connections require Evidence Studio on the Enterprise plan.
`evidence link`
Attach this repo to an existing Studio project. The project's content becomes git-managed — the next git push overwrites it with the repo's contents.
evidence link my-project
evidence link --project my-project| Option | Description |
|---|---|
--project |
Studio project to attach, by slug or id. Also accepted as a positional argument. |
--branch |
Branch to publish from (default: auto-detected). |
--root-directory |
Project root within the repo (default: auto-detected). |
The repository host must match the GitHub host connected to the Evidence organization.
`evidence unlink`
Disconnect this repo from its Studio project. The org's GitHub App install and the project itself are left intact.
evidence unlink
evidence unlink --project my-project| Option | Description |
|---|---|
--project |
Studio project to unlink, by slug or id. Inferred from the repo's binding when omitted. Also accepted as a positional argument. |
Data
`evidence query`
Run a SQL query against connected data sources.
evidence query "SELECT * FROM orders LIMIT 10"
evidence query --file query.sql
cat query.sql | evidence query -| Option | Description |
|---|---|
--format |
Output format: json (default), jsonl, table, csv |
--limit, -l |
Limit number of rows |
--quiet, -q |
Suppress status messages |
--columns |
Output only column names and types |
--file |
Read SQL from a file |
--output, -o |
Write results to a file |
`evidence tables`
List available data tables.
evidence tables`evidence describe`
Show schema for a table.
evidence describe demo_daily_orders`evidence schema`
Show all tables with their columns.
evidence schema`evidence connectors`
List all connectors with health status.
evidence connectors
evidence connectors --jsonShows connector name, type, status, last sync, schedule, table count, and reference count for all connector types.
`evidence models`
List all models with refresh status.
evidence models
evidence models --jsonShows model name, status, last refresh time, and schedule.
`evidence lineage`
Show where connections and tables are used across projects, pages, models, and queries.
evidence lineage
evidence lineage table orders
evidence lineage --branch dev/alex
evidence lineage --json| Option | Description |
|---|---|
table <name> |
Show where a specific table is referenced |
--branch |
Query a specific branch (default: main) |
--json |
Output JSON for tooling |
Project
`evidence validate`
Validate all markdown files in the current project.
evidence validate
evidence validate --json`evidence docs`
Browse and search component documentation.
evidence docs # Overview
evidence docs search "chart" # Search docs
evidence docs component bar_chart # Component detailsAuthentication
`evidence signup`
Create an account and workspace. Opens Evidence in your browser; once done, run evidence login to connect the CLI.
evidence signup`evidence login`
Authenticate with Evidence Studio using your browser.
evidence login`evidence logout`
Clear stored credentials.
evidence logout`evidence whoami`
Show the currently authenticated user.
evidence whoami`evidence orgs`
List available organizations.
evidence orgs`evidence switch`
Switch to a different organization.
evidence switch "My Organization"`evidence token`
Generate a token for headless/CI use. Prints an EVIDENCE_AUTH_TOKEN value you can set as an environment variable to authenticate without an interactive login. Requires an active login and a selected organization.
evidence tokenUtility
`evidence version`
Print the current CLI version.
evidence version`evidence upgrade`
Upgrade to the latest CLI version.
evidence upgrade`evidence help`
Show help and available commands.
evidence help`evidence telemetry`
Show or change anonymous usage reporting. See Telemetry for what is sent.
evidence telemetry # same as status
evidence telemetry status
evidence telemetry disable
evidence telemetry enableSelf-hosting
`evidence serve`
Serve the project as a self-hosted site. Runs the built-in production server — no Evidence Studio account or session required.
evidence serve
evidence serve --port 8080
evidence serve --host 0.0.0.0| Option | Description |
|---|---|
--port, -p |
Port to run on (default: 3000) |
--host |
Bind address (default: 127.0.0.1) |
--open |
Open the browser after starting (default: off) |
Requires a direct connector configured in connection.yaml — the managed query engine is not supported for self-hosting.
To serve beyond localhost, set EVIDENCE_BASIC_USER and EVIDENCE_BASIC_PASSWORD to protect the site with HTTP Basic Auth. The server refuses to start on a non-localhost --host without these set.
On a trusted private network (VPN, Tailscale, internal VPC), EVIDENCE_AUTH_DISABLED=true skips authentication entirely.
Cli Connections (`docs/cli/connections.mdx`)
title: 'Connect your CLI to your data' sidebarTitle: 'Connections'
How the CLI gets data depends on how your project is set up.
Evidence Warehouse
If you use the Evidence Warehouse, run evidence login. No further setup is needed.
Other warehouses
If you use a direct connection to a warehouse you own, add a connection.yaml file to the root of your project. When this file is present, the CLI queries your warehouse directly from your machine — no login required.
Scaffold with `evidence init`
The quickest way to create a connection.yaml is to pass --warehouse to evidence init:
evidence init my-project --warehouse snowflake
# or
evidence init my-project --warehouse bigqueryThis writes a connection.yaml in the project root pre-filled with placeholder values, and adds it to .gitignore (the file holds credentials — keep it out of version control).
Fill in the placeholders with your own connection details. If --warehouse is omitted, no connection.yaml is written and the project uses the Evidence Warehouse.
Configuration
Provide exactly one of `password`, `private_key`, or `private_key_path`. Paths (`private_key_path`) are resolved relative to `connection.yaml`.
See the [Snowflake direct connector](/direct-connectors/snowflake) for the full field reference and how to create a read-only Snowflake user.Provide exactly one of `keyfile` or `keyfile_json`. The `keyfile` path is resolved relative to `connection.yaml`.
See the [BigQuery direct connector](/direct-connectors/bigquery) for the full field reference and the service-account setup steps.Components Accordion (`docs/components/accordion.mdx`)
title: Accordion keywords: ["collapsible","expandable","disclosure"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% accordion %}
...
{% /accordion %}Attributes
Allowed values:
defaultwellcard
Allowed Children
Components Accordion Item (`docs/components/accordion_item.mdx`)
title: Accordion Item
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% accordion %}
{% accordion_item title="Section 1" open=true %}
Content for section 1.
{% /accordion_item %}
{% accordion_item title="Section 2" %}
Content for section 2.
{% /accordion_item %}
{% accordion_item title="Section 3" icon="settings" %}
Content for section 3 with an icon.
{% /accordion_item %}
{% /accordion %}Examples
Accordion with Items
{% accordion %}
{% accordion_item title="Section 1" open=true %}
Content for section 1.
{% /accordion_item %}
{% accordion_item title="Section 2" %}
Content for section 2.
{% /accordion_item %}
{% accordion_item title="Section 3" icon="settings" %}
Content for section 3 with an icon.
{% /accordion_item %}
{% /accordion %}Attributes
Allowed values:
trending-uptrending-downclockcalendarcheckxinfoalert-circlehelp-circleeyeeye-offuseruserssettingscogplusminusupdownrightleftstarheartsearchfilefile-texthomemailfiltersharebelltrashcredit-cardglobekeycroissantmaprotaterewindbankreceiptactivitychart-columnchart-piechart-no-axes-combinedgoalrockettrophyapplecookiedonutbeefcakesouputensilsmilknutpyramidtrianglearrow-downarrow-leftarrow-rightarrow-upchevron-downchevron-leftchevron-rightchevron-upchevrons-downchevrons-leftchevrons-rightchevrons-upmenuexternal-linkcheck-circlex-circleedittrash-2copysavedownloaduploadsendrefreshredoundofolderfolder-openimagefile-imageuser-plususer-minususer-checklockunlocklog-inlog-outmessage-squaremessage-circlephonephone-callbell-offvideovideo-offplaypausestopskip-backskip-forwardvolumevolume-1volume-2volume-offvolume-xbookmarktaglinkunlinkshare-2alert-triangleloadermore-verticalmore-horizontalgridlistmaximizeminimizezoom-inzoom-outthumbs-upthumbs-downshopping-cartdollar-signcameraprintermonitorsmartphonelaptopcalculatorcloud-sun-rainsun-snowthermometer-sunthermometer-snowflakecloudycloud-rain-windcloud-rainwindsuncloud-snowthermometercloud-drizzlecloud-suncloudcloud-lightningsnowflakeflameatomfuelmagnetfactorytree-deciduouswaypointsplugdambattery
Allowed Parents
Components Accordion Title (`docs/components/accordion_title.mdx`)
title: Accordion Title
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
Examples
Accordion item with a big_value in the title
{% accordion %}
{% accordion_item %}
{% accordion_title padding_top=8 padding_bottom=8 %}
{% big_value data="orders" value="sum(sales)" fmt="usd" /%}
{% /accordion_title %}
Body content for this section.
{% /accordion_item %}
{% /accordion %}Attributes
Allowed Parents
Components Area (`docs/components/area.mdx`)
title: Area
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% combo_chart data="demo.daily_orders" x="date" date_grain="month" %}
{% area y="sum(total_sales)" /%}
{% /combo_chart %}Examples
Basic Area Series
{% combo_chart data="demo.daily_orders" x="date" date_grain="month" %}
{% area y="sum(total_sales)" /%}
{% /combo_chart %}Unstacked Areas
{% combo_chart data="demo.daily_orders" x="date" date_grain="month" %}
{% area y="sum(total_sales)" series="category" stacked=false /%}
{% /combo_chart %}Area with Gradient Fill
{% combo_chart data="demo.daily_orders" x="date" date_grain="month" %}
{% area y="sum(total_sales)" stacked=false options={
gradient=true
} /%}
{% /combo_chart %}Attributes
Allowed values:
y1y2
Example:
data_labels={
position = "above"
fmt = "date"
size = 0
distance = 0
rotate = 0
color = "string"
border_color = "string"
show_overlap = true
}Attributes:
- position:
string- Position the label relative to its data point- Allowed values:
abovebelowleftrightmiddle
- Allowed values:
- fmt:
string- Format the label value. Defaults to series or axis fmt.- Allowed values: See Value Formatting for all available formats.
- size:
number- Font size in px - distance:
number- How far the label is from the data point - rotate:
number- Rotate each label (degrees) - color:
string- Change the text color of the labels - border_color:
string- Change the border color surrounding text labels, defaults to chart background - show_overlap:
boolean- Show labels for every point even when they overlap
Example:
options={
step = "start"
smooth = true
gradient = true
}Attributes:
- step:
string- Show a stepped line rather than a smooth line between points and control where the step happens (start, middle, or end)- Allowed values:
startmiddleend
- Allowed values:
- smooth:
boolean - gradient:
boolean- Apply a color-to-transparent gradient fill to the area, fading from the series color at the top to transparent at the bottom
Example:
echarts_options={
endLabel={ show=true }
markPoint={ data=[{ type="max" }, { type="min" }] }
}Allowed Parents
Components Area Chart (`docs/components/area_chart.mdx`)
title: Area Chart keywords: ["area graph","area plot","stacked area"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% area_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
date_grain="month"
/%}Examples
Basic Usage
{% area_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
date_grain="month"
/%}Area Chart with Series
{% area_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
series="category"
date_grain="month"
/%}Area Chart with Formatting
{% area_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
y_fmt="usd"
title="Sales Over Time"
subtitle="Monthly sales performance"
date_grain="month"
/%}100% Stacked Area Chart
{% area_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
series="category"
stacked="100%"
date_grain="month"
title="Sales Distribution by Category"
/%}Revenue by Day of Week
{% area_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
y_fmt="usd"
date_grain="day of week"
title="Revenue by Day of Week"
subtitle="Weekday vs weekend sales patterns"
/%}Seasonality Analysis (Month of Year)
{% area_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
y_fmt="usd"
date_grain="month of year"
title="Seasonality Analysis"
subtitle="Sales patterns across months"
/%}Quarterly Trends
{% area_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
y_fmt="usd"
date_grain="quarter of year"
title="Quarterly Performance"
subtitle="Q1 through Q4 comparison"
/%}Monthly Billing Cycle Patterns
{% area_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
y_fmt="usd"
date_grain="day of month"
title="Daily Revenue by Day of Month"
subtitle="Identify billing cycle patterns"
/%}Week Number Analysis
{% area_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
y_fmt="usd"
date_grain="week of year"
title="Revenue by Week of Year"
subtitle="Weekly performance across the year"
/%}Day of Year Analysis
{% area_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
y_fmt="usd"
date_grain="day of year"
title="Revenue by Day of Year"
subtitle="Identify patterns across 365 days"
/%}Area Chart with Gradient Fill
{% area_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
y_fmt="usd"
date_grain="month"
stacked=false
area_options={
gradient=true
}
/%}Attributes
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Allowed values:
dayweekmonthquarteryearhourday of weekday of monthday of yearweek of yearmonth of yearquarter of year
Allowed values:
connectgapszero
Allowed values:
ascdescdata
Example:
y_axis_options={
title = "string"
title_position = "top"
ticks = true
baseline = true
labels = true
gridlines = true
min = 0
max = 0
fit_to_data = true
interval = 0
}Attributes:
- title:
string - title_position:
string- Position of the axis title. "top" places it horizontally at the top, "side" places it vertically along the axis. Defaults to "side" for 100% stacked charts, "top" otherwise.- Allowed values:
topside
- Allowed values:
- ticks:
boolean - baseline:
boolean - labels:
boolean- Show/hide axis labels - gridlines:
boolean- Show/hide gridlines - min:
number- Minimum value for this axis (number for numeric axes, date string for date axes) - max:
number- Maximum value for this axis (number for numeric axes, date string for date axes) - fit_to_data:
boolean- Fit the axis to the data instead of including 0 - interval:
number- Interval between axis ticks for numeric axes. This option is a suggestion, the actual interval may differ.
Example:
y2_axis_options={
title = "string"
title_position = "top"
ticks = true
baseline = true
labels = true
gridlines = true
min = 0
max = 0
fit_to_data = true
interval = 0
}Attributes:
- title:
string - title_position:
string- Position of the axis title. "top" places it horizontally at the top, "side" places it vertically along the axis. Defaults to "side" for 100% stacked charts, "top" otherwise.- Allowed values:
topside
- Allowed values:
- ticks:
boolean - baseline:
boolean - labels:
boolean- Show/hide axis labels - gridlines:
boolean- Show/hide gridlines - min:
number- Minimum value for this axis (number for numeric axes, date string for date axes) - max:
number- Maximum value for this axis (number for numeric axes, date string for date axes) - fit_to_data:
boolean- Fit the axis to the data instead of including 0 - interval:
number- Interval between axis ticks for numeric axes. This option is a suggestion, the actual interval may differ.
Example:
x_axis_options={
title = "string"
show_title = true
label_wrap = true
ticks = true
baseline = true
labels = true
gridlines = true
min = 0
max = 0
fit_to_data = true
min_interval = "year"
max_interval = "year"
interval = 0
label_rotate = 0
title_arrow = true
max_label_length = 0
}Attributes:
- title:
string - show_title:
boolean- Whentrue, renders the auto-derived axis title (the x column name) below the chart. Ignored whentitleis set explicitly. Defaults tofalse— auto-derived column-name titles usually read as visual noise and the axis labels speak for themselves. - label_wrap:
boolean - ticks:
boolean - baseline:
boolean - labels:
boolean- Show/hide axis labels - gridlines:
boolean- Show/hide gridlines - min:
number- Minimum value for this axis (number for numeric axes, date string for date axes) - max:
number- Maximum value for this axis (number for numeric axes, date string for date axes) - fit_to_data:
boolean- Fit the axis to the data instead of including 0. Defaults to true for numeric x-axes, false otherwise. - min_interval:
string- Minimum interval between axis ticks for time-based axes. This option is a suggestion, the actual interval may differ.- Allowed values:
yearquartermonthweekdayhour
- Allowed values:
- max_interval:
string- Maximum interval between axis ticks for time-based axes. This option is a suggestion, the actual interval may differ.- Allowed values:
yearquartermonthweekdayhour
- Allowed values:
- interval:
number- Interval between axis ticks for numeric axes. This option is a suggestion, the actual interval may differ. - label_rotate:
number- Rotation angle of axis label in degrees. Positive values rotate clockwise, negative values rotate counter-clockwise. - title_arrow:
boolean- Show/hide the arrow (→) on the axis title - max_label_length:
number- Maximum character length for axis labels. Labels exceeding this length will be truncated with an ellipsis. Defaults to 20 characters when labels are rotated.
Allowed values:
topbottom
Example:
chart_options={
color_palette = ["#3b82f6", "#8b5cf6", "#ec4899"]
series_colors = {
"Series A" = "#3b82f6"
"Series B" = "#10b981"
"Series C" = "#f59e0b"
}
zoom = true
top_padding = 0
}Attributes:
- color_palette:
array of strings- Array of hex color codes to use for series colors - series_colors:
map of key-value pairs- Map of series names to hex color codes for custom series coloring - zoom:
boolean- Enables zoom by dragging on the chart area - top_padding:
number- Additional padding (in px) above the chart area to prevent labels from being cut off
Example:
echarts_options={
tooltip={ position="top" }
dataZoom=[{ type="slider" }]
}Example:
echarts_series_options={
itemStyle={ borderRadius=8 }
markLine={ data=[{ type="average" }] }
}Example:
data_labels={
position = "above"
fmt = "date"
size = 0
distance = 0
rotate = 0
color = "string"
border_color = "string"
show_overlap = true
}Attributes:
- position:
string- Position the label relative to its data point- Allowed values:
abovebelowleftrightmiddle
- Allowed values:
- fmt:
string- Format the label value. Defaults to series or axis fmt.- Allowed values: See Value Formatting for all available formats.
- size:
number- Font size in px - distance:
number- How far the label is from the data point - rotate:
number- Rotate each label (degrees) - color:
string- Change the text color of the labels - border_color:
string- Change the border color surrounding text labels, defaults to chart background - show_overlap:
boolean- Show labels for every point even when they overlap
Example:
options={
step = "start"
smooth = true
gradient = true
}Attributes:
- step:
string- Show a stepped line rather than a smooth line between points and control where the step happens (start, middle, or end)- Allowed values:
startmiddleend
- Allowed values:
- smooth:
boolean - gradient:
boolean- Apply a color-to-transparent gradient fill to the area, fading from the series color at the top to transparent at the bottom
Example:
area_options={
step = "start"
smooth = true
gradient = true
}Attributes:
- step:
string- Show a stepped line rather than a smooth line between points and control where the step happens (start, middle, or end)- Allowed values:
startmiddleend
- Allowed values:
- smooth:
boolean - gradient:
boolean- Apply a color-to-transparent gradient fill to the area, fading from the series color at the top to transparent at the bottom
Allowed Children
Components Area Layer (`docs/components/area_layer.mdx`)
title: Area Layer
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% map %}
{% area_layer
geography="us_states"
match_by="name"
data="state_sales"
area_id="state"
value="sum(sales)"
/%}
{% /map %}Examples
US States by Name
{% map %}
{% area_layer
geography="us_states"
match_by="name"
data="state_sales"
area_id="state"
value="sum(sales)"
/%}
{% /map %}US States by Abbreviation
{% map %}
{% area_layer
geography="us_states"
match_by="abbr"
data="state_sales"
area_id="state_abbr"
value="sum(sales)"
/%}
{% /map %}US Counties by State + County
{% map %}
{% area_layer
geography="us_counties"
match_by="state-county"
data="county_sales"
area_id="state || '-' || county"
value="sum(sales)"
/%}
{% /map %}US Counties by FIPS
{% map %}
{% area_layer
geography="us_counties"
match_by="fips"
data="county_sales"
area_id="state_fips || county_fips"
value="sum(sales)"
/%}
{% /map %}Custom GeoJSON
{% area_layer
geojson_url="https://example.com/custom.geojson"
geojson_id="id"
data="my_data"
area_id="region_id"
value="sum(sales)"
/%}Diverging Scale Centered at 0
{% map %}
{% area_layer
geography="us_states"
match_by="abbr"
data="state_growth"
area_id="state_abbr"
value="growth_pct"
color_scale=["#d73027", "#ffffbf", "#1a9850"]
min=-100
max=100
midpoint=0
/%}
{% /map %}Custom Breakpoints
{% map %}
{% area_layer
geography="us_states"
match_by="abbr"
data="state_sales"
area_id="state_abbr"
value="sum(sales)"
color_stops=[
{ value=0 color="#ffffff" },
{ value=10000 color="#f59e0b" },
{ value=50000 color="#1a9850" }
]
/%}
{% /map %}Attributes
Allowed values:
us_statesus_counties
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Allowed Parents
Components Audio (`docs/components/audio.mdx`)
title: Audio keywords: ["sound","music","podcast","mp3","wav","media","player"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% audio url="https://files.example.com/podcast/episode-01.mp3" /%}Examples
Basic Audio Player
{% audio url="https://files.example.com/podcast/episode-01.mp3" /%}WAV File with Title
{% audio url="https://files.example.com/recordings/call.wav" type="wav" title="Call Recording" /%}Attributes
Components Bar (`docs/components/bar.mdx`)
title: Bar
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% combo_chart data="demo.daily_orders" x="date" date_grain="month" %}
{% bar y="sum(total_sales)" /%}
{% /combo_chart %}Examples
Basic Bar Series
{% combo_chart data="demo.daily_orders" x="date" date_grain="month" %}
{% bar y="sum(total_sales)" /%}
{% /combo_chart %}Unstacked Bars
{% combo_chart data="demo.daily_orders" x="date" date_grain="month" %}
{% bar y="sum(total_sales)" series="category" stacked=false /%}
{% /combo_chart %}Multiple Stacks
{% combo_chart data="demo.daily_orders" x="date" date_grain="month" %}
{% bar y="sum(total_sales)" stack_id="sales" /%}
{% bar y="sum(total_sales)+1000000" stack_id="sales" /%}
{% bar y="sum(quantity)" stack_id="quantity" /%}
{% /combo_chart %}Extra tooltip fields
{% combo_chart data="demo.daily_orders" x="date" date_grain="month" %}
{% bar
y="sum(total_sales)"
fmt="usd"
tooltip_fields=[
{ value="count(distinct order_id)" label="Orders" },
{ value="sum(total_sales) - lag(sum(total_sales)) over (order by date_trunc('month', date))" label="MoM Δ" fmt="usd" color_by_sign=true }
]
/%}
{% /combo_chart %}Attributes
Allowed values:
y1y2
Example:
data_labels={
position = "above"
fmt = "date"
size = 0
distance = 0
rotate = 0
color = "string"
border_color = "string"
show_overlap = true
}Attributes:
- position:
string- Position the label relative to its data point- Allowed values:
abovebelowleftrightmiddle
- Allowed values:
- fmt:
string- Format the label value. Defaults to series or axis fmt.- Allowed values: See Value Formatting for all available formats.
- size:
number- Font size in px - distance:
number- How far the label is from the data point - rotate:
number- Rotate each label (degrees) - color:
string- Change the text color of the labels - border_color:
string- Change the border color surrounding text labels, defaults to chart background - show_overlap:
boolean- Show labels for every point even when they overlap
Attributes:
- color:
string - opacity:
number- Between 0 and 1
Example:
echarts_options={
endLabel={ show=true }
markPoint={ data=[{ type="max" }, { type="min" }] }
}Allowed Parents
Components Bar Chart (`docs/components/bar_chart.mdx`)
title: Bar Chart keywords: ["column chart","bar graph","bar plot"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% bar_chart
data="demo.daily_orders"
x="category"
y="sum(total_sales)"
/%}Examples
Basic Usage
{% bar_chart
data="demo.daily_orders"
x="category"
y="sum(total_sales)"
/%}Bar Chart with Date Grain
{% bar_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
date_grain="month"
/%}Sorting by Value
{% bar_chart
data="demo.daily_orders"
x="category"
y="sum(total_sales)"
order="sum(total_sales) desc"
/%}Custom Category Order
{% bar_chart
data="demo.daily_orders"
x="category"
y="sum(total_sales)"
x_sort=["Clothing", "Home", "Sports", "Electronics"]
/%}Bar Chart with Series Colors
{% bar_chart
data="demo.daily_orders"
x="category"
y="sum(total_sales)"
series="case when sum(total_sales) > 7000 then '>$7k' when sum(total_sales) > 3500 then '>$3.5k' else '<$3.5k' end"
title="Sales by Category and Performance"
chart_options={
series_colors={
">$7k"="#22c55e"
">$3.5k"="#f59e0b"
"<$3.5k"="#ef4444"
}
}
/%}100% Stacked Bar Chart
{% bar_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
series="category"
stacked="100%"
date_grain="month"
title="Sales Distribution by Category"
/%}Revenue by Day of Week
{% bar_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
y_fmt="usd"
date_grain="day of week"
title="Revenue by Day of Week"
subtitle="Weekday vs weekend sales patterns"
/%}Seasonality Analysis (Month of Year)
{% bar_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
y_fmt="usd"
date_grain="month of year"
title="Seasonality Analysis"
subtitle="Sales patterns across months"
/%}Quarterly Trends
{% bar_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
y_fmt="usd"
date_grain="quarter of year"
title="Quarterly Performance"
subtitle="Q1 through Q4 comparison"
/%}Monthly Billing Cycle Patterns
{% bar_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
y_fmt="usd"
date_grain="day of month"
title="Daily Revenue by Day of Month"
subtitle="Identify billing cycle patterns"
/%}Week Number Analysis
{% bar_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
y_fmt="usd"
date_grain="week of year"
title="Revenue by Week of Year"
subtitle="Weekly performance across the year"
/%}Day of Year Analysis
{% bar_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
y_fmt="usd"
date_grain="day of year"
title="Revenue by Day of Year"
subtitle="Identify patterns across 365 days"
/%}Extra tooltip fields
{% bar_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
y_fmt="usd"
date_grain="month"
tooltip_fields=[
{ value="count(distinct order_id)" label="Orders" },
{ value="sum(total_sales) / nullif(count(distinct order_id), 0)" label="Avg order" fmt="usd" }
]
/%}Raw ECharts overrides (rounded bars, background track)
{% bar_chart
data="demo.daily_orders"
x="category"
y="sum(total_sales)"
echarts_series_options={
itemStyle={ borderRadius=[6, 6, 0, 0] }
showBackground=true
backgroundStyle={ color="rgba(0,0,0,0.04)" }
}
/%}Attributes
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Allowed values:
dayweekmonthquarteryearhourday of weekday of monthday of yearweek of yearmonth of yearquarter of year
Allowed values:
connectgapszero
Allowed values:
ascdescdata
Example:
y_axis_options={
title = "string"
title_position = "top"
ticks = true
baseline = true
labels = true
gridlines = true
min = 0
max = 0
fit_to_data = true
interval = 0
}Attributes:
- title:
string - title_position:
string- Position of the axis title. "top" places it horizontally at the top, "side" places it vertically along the axis. Defaults to "side" for 100% stacked charts, "top" otherwise.- Allowed values:
topside
- Allowed values:
- ticks:
boolean - baseline:
boolean - labels:
boolean- Show/hide axis labels - gridlines:
boolean- Show/hide gridlines - min:
number- Minimum value for this axis (number for numeric axes, date string for date axes) - max:
number- Maximum value for this axis (number for numeric axes, date string for date axes) - fit_to_data:
boolean- Fit the axis to the data instead of including 0 - interval:
number- Interval between axis ticks for numeric axes. This option is a suggestion, the actual interval may differ.
Example:
y2_axis_options={
title = "string"
title_position = "top"
ticks = true
baseline = true
labels = true
gridlines = true
min = 0
max = 0
fit_to_data = true
interval = 0
}Attributes:
- title:
string - title_position:
string- Position of the axis title. "top" places it horizontally at the top, "side" places it vertically along the axis. Defaults to "side" for 100% stacked charts, "top" otherwise.- Allowed values:
topside
- Allowed values:
- ticks:
boolean - baseline:
boolean - labels:
boolean- Show/hide axis labels - gridlines:
boolean- Show/hide gridlines - min:
number- Minimum value for this axis (number for numeric axes, date string for date axes) - max:
number- Maximum value for this axis (number for numeric axes, date string for date axes) - fit_to_data:
boolean- Fit the axis to the data instead of including 0 - interval:
number- Interval between axis ticks for numeric axes. This option is a suggestion, the actual interval may differ.
Example:
x_axis_options={
title = "string"
show_title = true
label_wrap = true
ticks = true
baseline = true
labels = true
gridlines = true
min = 0
max = 0
fit_to_data = true
min_interval = "year"
max_interval = "year"
interval = 0
label_rotate = 0
title_arrow = true
max_label_length = 0
}Attributes:
- title:
string - show_title:
boolean- Whentrue, renders the auto-derived axis title (the x column name) below the chart. Ignored whentitleis set explicitly. Defaults tofalse— auto-derived column-name titles usually read as visual noise and the axis labels speak for themselves. - label_wrap:
boolean - ticks:
boolean - baseline:
boolean - labels:
boolean- Show/hide axis labels - gridlines:
boolean- Show/hide gridlines - min:
number- Minimum value for this axis (number for numeric axes, date string for date axes) - max:
number- Maximum value for this axis (number for numeric axes, date string for date axes) - fit_to_data:
boolean- Fit the axis to the data instead of including 0. Defaults to true for numeric x-axes, false otherwise. - min_interval:
string- Minimum interval between axis ticks for time-based axes. This option is a suggestion, the actual interval may differ.- Allowed values:
yearquartermonthweekdayhour
- Allowed values:
- max_interval:
string- Maximum interval between axis ticks for time-based axes. This option is a suggestion, the actual interval may differ.- Allowed values:
yearquartermonthweekdayhour
- Allowed values:
- interval:
number- Interval between axis ticks for numeric axes. This option is a suggestion, the actual interval may differ. - label_rotate:
number- Rotation angle of axis label in degrees. Positive values rotate clockwise, negative values rotate counter-clockwise. - title_arrow:
boolean- Show/hide the arrow (→) on the axis title - max_label_length:
number- Maximum character length for axis labels. Labels exceeding this length will be truncated with an ellipsis. Defaults to 20 characters when labels are rotated.
Allowed values:
topbottom
Example:
chart_options={
color_palette = ["#3b82f6", "#8b5cf6", "#ec4899"]
series_colors = {
"Series A" = "#3b82f6"
"Series B" = "#10b981"
"Series C" = "#f59e0b"
}
zoom = true
top_padding = 0
}Attributes:
- color_palette:
array of strings- Array of hex color codes to use for series colors - series_colors:
map of key-value pairs- Map of series names to hex color codes for custom series coloring - zoom:
boolean- Enables zoom by dragging on the chart area - top_padding:
number- Additional padding (in px) above the chart area to prevent labels from being cut off
Example:
echarts_options={
tooltip={ position="top" }
dataZoom=[{ type="slider" }]
}Example:
echarts_series_options={
itemStyle={ borderRadius=8 }
markLine={ data=[{ type="average" }] }
}Example:
data_labels={
position = "above"
fmt = "date"
size = 0
distance = 0
rotate = 0
color = "string"
border_color = "string"
show_overlap = true
}Attributes:
- position:
string- Position the label relative to its data point- Allowed values:
abovebelowleftrightmiddle
- Allowed values:
- fmt:
string- Format the label value. Defaults to series or axis fmt.- Allowed values: See Value Formatting for all available formats.
- size:
number- Font size in px - distance:
number- How far the label is from the data point - rotate:
number- Rotate each label (degrees) - color:
string- Change the text color of the labels - border_color:
string- Change the border color surrounding text labels, defaults to chart background - show_overlap:
boolean- Show labels for every point even when they overlap
Attributes:
- color:
string - opacity:
number- Between 0 and 1
Attributes:
- color:
string - opacity:
number- Between 0 and 1
Allowed Children
Components Benchmark Comparison (`docs/components/benchmark_comparison.mdx`)
title: Benchmark Comparison
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% benchmark_comparison
name="vs Franchisees"
agg="avg"
subject="store_id"
where="ownership_type = 'franchise'"
/%}Examples
Compare vs Franchisees
{% benchmark_comparison
name="vs Franchisees"
agg="avg"
subject="store_id"
where="ownership_type = 'franchise'"
/%}Compare vs Full Network
{% benchmark_comparison
name="vs Full Network"
agg="avg"
subject="store_id"
/%}Compare vs Region Average
{% benchmark_comparison
name="vs Region Avg"
agg="avg"
subject="store_id"
within=["region"]
/%}Attributes
Allowed values:
avgmedianminmaxsumcountcount_distinct
Allowed values:
pctabscompared_value
Allowed Parents
Components Big Value (`docs/components/big_value.mdx`)
title: Big Value keywords: ["KPI","metric","big number","scorecard","headline number"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% big_value
data="demo.daily_orders"
value="sum(total_sales)"
fmt="usd1m"
/%}Examples
Basic Usage
{% big_value
data="demo.daily_orders"
value="sum(total_sales)"
fmt="usd1m"
/%}Comparison
{% big_value
data="demo.daily_orders"
value="sum(total_sales)"
fmt="usd1m"
date_range={
date="date"
range="last 12 months"
}
comparison={
compare_vs="prior year"
}
/%}Sparkline
{% big_value
data="demo.daily_orders"
value="sum(total_sales)"
fmt="usd1m"
sparkline={
type="line"
x="date"
}
/%}Text Size
{% big_value
data="demo.daily_orders"
value="sum(total_sales)"
fmt="usd1m"
text_size="4xl"
/%}Attributes
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Example:
comparison={
compare_vs = "prior year"
display_type = "compared_value"
target = "string"
benchmark = {
agg = "avg"
subject = "store_name"
within = ["region"]
}
hide_pct = true
pct_fmt = "string"
abs_fmt = "string"
text = "string"
delta = true
down_is_good = true
neutral_range = []
}Attributes:
- compare_vs:
string- Type of comparison to perform. Options: prior year (same period last year), prior period (previous period of same duration), target (compare against a target value), benchmark (compare against group average/aggregate)- Allowed values:
prior yearprior periodtargetbenchmark
- Allowed values:
- display_type:
string- What to display for comparison. Options: compared_value (comparison period value), abs (absolute change), pct (percentage change). Default: pct- Allowed values:
compared_valueabspct
- Allowed values:
- target:
string- Target value for target comparison. Can be a column name, aggregation (e.g., "sum(target_sales)"), or literal value. - benchmark:
options group- Options:
- agg:
string- Aggregation function to apply across benchmark group. Options: avg (average), median, min, max, sum, count, count_distinct- Allowed values:
avgmedianminmaxsumcountcount_distinct
- Allowed values:
- subject:
string- Column or expression that defines individual entities in the benchmark (e.g., "store_name", "customer_id"). Required for single-value components. - value:
string- Optional column or expression to use for benchmark calculation. If not specified, uses the main value column. Useful if you have a pre-aggregated benchmark table for RLS reasons. - within:
array of strings- Dimension columns to group the benchmark by (e.g., ["region"]). Leave empty for dataset-wide benchmark. - where:
string- SQL WHERE clause to filter which entities are included in the benchmark - exclude_self:
boolean- Exclude the current row from its own benchmark calculation (table context only). Default: false
- agg:
- Options:
- hide_pct:
boolean- Hide the percentage change line in comparison tooltips - pct_fmt:
string- Format code for percentage values in comparison tooltips - abs_fmt:
string- Format code for absolute values in comparison tooltips - text:
string- Text displayed after the comparison value - delta:
boolean- Whether to display the comparison as a delta - down_is_good:
boolean- Whether a decrease is considered positive. Defaults to the page'sdown_is_goodsetting. - neutral_range:
array- Range [min, max] for neutral values. Use null for infinity (e.g., [null, 0] means anything ≤ 0 is neutral)
Example:
sparkline={
type = "line"
color = "string"
x = "string"
y_fmt = "string"
x_fmt = "string"
fit_to_data = true
connect_group = "string"
date_grain = "year"
date_range = {
range = "today"
date = "string"
}
}Attributes:
- type:
string- The type of sparkline to display- Allowed values:
lineareabar
- Allowed values:
- color:
string- Color for the sparkline - x:
string- X column for the sparkline - y_fmt:
string- Value format for the sparkline tooltips - x_fmt:
string- Date format for the sparkline tooltips - fit_to_data:
boolean- Whether to fit the Y axis scale to the data range - connect_group:
string- Connect group for the sparkline - date_grain:
string- Time grain for the sparkline data points- Allowed values:
yearquartermonthweekdayhourminute
- Allowed values:
- date_range:
options group- Options:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
- range:
- Options:
Allowed values:
smbaselgxl2xl3xl4xl5xl
Components Bubble (`docs/components/bubble.mdx`)
title: Bubble
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% combo_chart data="demo.daily_orders" x="date" date_grain="month" %}
{% bubble
y="avg(avg_transaction_value)"
size="sum(transactions)"
/%}
{% /combo_chart %}Examples
Basic Bubble Series
{% combo_chart data="demo.daily_orders" x="date" date_grain="month" %}
{% bubble
y="avg(avg_transaction_value)"
size="sum(transactions)"
/%}
{% /combo_chart %}Attributes
Allowed values:
y1y2
Example:
data_labels={
position = "above"
fmt = "date"
size = 0
distance = 0
rotate = 0
color = "string"
border_color = "string"
show_overlap = true
}Attributes:
- position:
string- Position the label relative to its data point- Allowed values:
abovebelowleftrightmiddle
- Allowed values:
- fmt:
string- Format the label value. Defaults to series or axis fmt.- Allowed values: See Value Formatting for all available formats.
- size:
number- Font size in px - distance:
number- How far the label is from the data point - rotate:
number- Rotate each label (degrees) - color:
string- Change the text color of the labels - border_color:
string- Change the border color surrounding text labels, defaults to chart background - show_overlap:
boolean- Show labels for every point even when they overlap
Example:
echarts_options={
endLabel={ show=true }
markPoint={ data=[{ type="max" }, { type="min" }] }
}Allowed Parents
Components Bubble Chart (`docs/components/bubble_chart.mdx`)
title: Bubble Chart keywords: ["bubble plot","bubble graph"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% bubble_chart
data="demo.daily_orders"
x="sum(total_sales)"
y="avg(avg_transaction_value)"
size="sum(transactions)"
series="category"
/%}Examples
Basic Usage
{% bubble_chart
data="demo.daily_orders"
x="sum(total_sales)"
y="avg(avg_transaction_value)"
size="sum(transactions)"
series="category"
/%}Bubble Chart with Formatting
{% bubble_chart
data="demo.daily_orders"
x="sum(total_sales)"
y="avg(avg_transaction_value)"
size="sum(transactions)"
x_fmt="usd"
y_fmt="usd"
title="Sales Performance Analysis"
subtitle="Bubble size represents transaction count"
series="category"
/%}Bubble Chart with Point Titles
{% bubble_chart
data="demo.daily_orders"
x="sum(total_sales)"
y="avg(avg_transaction_value)"
size="sum(transactions)"
point_title="category"
x_fmt="usd"
y_fmt="usd"
/%}Attributes
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Allowed values:
dayweekmonthquarteryearhourday of weekday of monthday of yearweek of yearmonth of yearquarter of year
Allowed values:
connectgapszero
Allowed values:
ascdescdata
Example:
y_axis_options={
title = "string"
title_position = "top"
ticks = true
baseline = true
labels = true
gridlines = true
min = 0
max = 0
fit_to_data = true
interval = 0
}Attributes:
- title:
string - title_position:
string- Position of the axis title. "top" places it horizontally at the top, "side" places it vertically along the axis. Defaults to "side" for 100% stacked charts, "top" otherwise.- Allowed values:
topside
- Allowed values:
- ticks:
boolean - baseline:
boolean - labels:
boolean- Show/hide axis labels - gridlines:
boolean- Show/hide gridlines - min:
number- Minimum value for this axis (number for numeric axes, date string for date axes) - max:
number- Maximum value for this axis (number for numeric axes, date string for date axes) - fit_to_data:
boolean- Fit the axis to the data instead of including 0 - interval:
number- Interval between axis ticks for numeric axes. This option is a suggestion, the actual interval may differ.
Example:
y2_axis_options={
title = "string"
title_position = "top"
ticks = true
baseline = true
labels = true
gridlines = true
min = 0
max = 0
fit_to_data = true
interval = 0
}Attributes:
- title:
string - title_position:
string- Position of the axis title. "top" places it horizontally at the top, "side" places it vertically along the axis. Defaults to "side" for 100% stacked charts, "top" otherwise.- Allowed values:
topside
- Allowed values:
- ticks:
boolean - baseline:
boolean - labels:
boolean- Show/hide axis labels - gridlines:
boolean- Show/hide gridlines - min:
number- Minimum value for this axis (number for numeric axes, date string for date axes) - max:
number- Maximum value for this axis (number for numeric axes, date string for date axes) - fit_to_data:
boolean- Fit the axis to the data instead of including 0 - interval:
number- Interval between axis ticks for numeric axes. This option is a suggestion, the actual interval may differ.
Example:
x_axis_options={
title = "string"
show_title = true
label_wrap = true
ticks = true
baseline = true
labels = true
gridlines = true
min = 0
max = 0
fit_to_data = true
min_interval = "year"
max_interval = "year"
interval = 0
label_rotate = 0
title_arrow = true
max_label_length = 0
}Attributes:
- title:
string - show_title:
boolean- Whentrue, renders the auto-derived axis title (the x column name) below the chart. Ignored whentitleis set explicitly. Defaults tofalse— auto-derived column-name titles usually read as visual noise and the axis labels speak for themselves. - label_wrap:
boolean - ticks:
boolean - baseline:
boolean - labels:
boolean- Show/hide axis labels - gridlines:
boolean- Show/hide gridlines - min:
number- Minimum value for this axis (number for numeric axes, date string for date axes) - max:
number- Maximum value for this axis (number for numeric axes, date string for date axes) - fit_to_data:
boolean- Fit the axis to the data instead of including 0. Defaults to true for numeric x-axes, false otherwise. - min_interval:
string- Minimum interval between axis ticks for time-based axes. This option is a suggestion, the actual interval may differ.- Allowed values:
yearquartermonthweekdayhour
- Allowed values:
- max_interval:
string- Maximum interval between axis ticks for time-based axes. This option is a suggestion, the actual interval may differ.- Allowed values:
yearquartermonthweekdayhour
- Allowed values:
- interval:
number- Interval between axis ticks for numeric axes. This option is a suggestion, the actual interval may differ. - label_rotate:
number- Rotation angle of axis label in degrees. Positive values rotate clockwise, negative values rotate counter-clockwise. - title_arrow:
boolean- Show/hide the arrow (→) on the axis title - max_label_length:
number- Maximum character length for axis labels. Labels exceeding this length will be truncated with an ellipsis. Defaults to 20 characters when labels are rotated.
Allowed values:
topbottom
Example:
chart_options={
color_palette = ["#3b82f6", "#8b5cf6", "#ec4899"]
series_colors = {
"Series A" = "#3b82f6"
"Series B" = "#10b981"
"Series C" = "#f59e0b"
}
zoom = true
top_padding = 0
}Attributes:
- color_palette:
array of strings- Array of hex color codes to use for series colors - series_colors:
map of key-value pairs- Map of series names to hex color codes for custom series coloring - zoom:
boolean- Enables zoom by dragging on the chart area - top_padding:
number- Additional padding (in px) above the chart area to prevent labels from being cut off
Example:
echarts_options={
tooltip={ position="top" }
dataZoom=[{ type="slider" }]
}Example:
echarts_series_options={
itemStyle={ borderRadius=8 }
markLine={ data=[{ type="average" }] }
}Example:
data_labels={
position = "above"
fmt = "date"
size = 0
distance = 0
rotate = 0
color = "string"
border_color = "string"
show_overlap = true
}Attributes:
- position:
string- Position the label relative to its data point- Allowed values:
abovebelowleftrightmiddle
- Allowed values:
- fmt:
string- Format the label value. Defaults to series or axis fmt.- Allowed values: See Value Formatting for all available formats.
- size:
number- Font size in px - distance:
number- How far the label is from the data point - rotate:
number- Rotate each label (degrees) - color:
string- Change the text color of the labels - border_color:
string- Change the border color surrounding text labels, defaults to chart background - show_overlap:
boolean- Show labels for every point even when they overlap
Allowed Children
Components Button Group (`docs/components/button_group.mdx`)
title: Button Group keywords: ["radio buttons","segmented control","chip selector"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% button_group
id="category_filter"
data="demo.daily_orders"
value_column="category"
/%}
{% bar_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
filters=["category_filter"]
date_grain="month"
/%}Examples
Using `filters`
{% button_group
id="category_filter"
data="demo.daily_orders"
value_column="category"
/%}
{% bar_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
filters=["category_filter"]
date_grain="month"
/%}Using `where`
{% button_group
id="category_filter"
data="demo.daily_orders"
value_column="category"
/%}
{% bar_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
where="category = {{category_filter}}"
date_grain="month"
/%}Using Inline SQL
{% button_group
id="category_filter"
data="demo.daily_orders"
value_column="category"
/%}
```sql filtered_orders
select * from demo.daily_orders
where category = {{category_filter}}
```
{% table data="filtered_orders" /%}Vertical orientation inside a row
{% row %}
{% button_group
id="category_filter"
data="demo.daily_orders"
value_column="category"
orientation="vertical"
/%}
{% bar_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
filters=["category_filter"]
date_grain="month"
/%}
{% /row %}Attributes
Allowed values:
horizontalvertical
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Using the Filter Variable
Reference this filter using {{filter_id}}. The value returned depends on where you use it.
The examples below show values for three scenarios:
- No selection
- Single select: "Electronics" selected
- Multi select: "Sports" and "Home" selected (when
multiple=true)
| Context | Default Property | No Selection | Single Select | Multi Select |
|---|---|---|---|---|
| Inline SQL query | .selected |
'' |
'Electronics' |
('Sports', 'Home') |
where attribute |
.selected |
'' |
'Electronics' |
('Sports', 'Home') |
| Text / Markdown | .literal |
Electronics |
Sports, Home |
Available Properties
You can also access specific properties using {{filter_id.property}}:
.filter
Returns a complete SQL filter expression ready to use in WHERE clauses. Returns true when no value is selected.
{% button_group id="category_filter" data="products" value_column="category" /%}
```sql filtered_products
select * from products
where {{category_filter.filter}}
```| No Selection | Single Select | Multi Select |
|---|---|---|
true |
category = 'Electronics' |
category IN ('Sports', 'Home') |
.selected
Returns the selected value(s) wrapped in quotes, suitable for SQL comparisons. Returns an empty string when no value is selected.
{% button_group id="category_filter" data="products" value_column="category" /%}
```sql products_by_category
select * from products
where category = {{category_filter.selected}}
```| No Selection | Single Select | Multi Select |
|---|---|---|
'' |
'Electronics' |
('Sports', 'Home') |
.literal
Returns the raw unescaped selected value(s), useful for display in text or dynamic column selection.
{% button_group id="sort_column" data="products" value_column="column_name" /%}
```sql dynamic_sort
select * from products
order by {{sort_column.literal}}
```| No Selection | Single Select | Multi Select |
|---|---|---|
| `` | Electronics |
Sports, Home |
.label
Returns the display label for the selected option(s). Falls back to the value if no label is defined.
{% button_group id="category_filter" %}
{% option value="Electronics" label="Electronics" /%}
{% option value="Sports" label="Sports" /%}
{% option value="Home" label="Home" /%}
{% /button_group %}
Selected: {{category_filter.label}}| No Selection | Single Select | Multi Select |
|---|---|---|
| `` | Electronics |
Sports, Home |
.fmt
Returns the format string associated with the selected option. For multiple selections, returns the first format.
{% button_group id="metric_selector" %}
{% option value="revenue" label="Revenue" fmt="usd" /%}
{% option value="growth_rate" label="Growth Rate" fmt="pct1" /%}
{% /button_group %}
{% big_value data={metrics} value=value fmt={{metric_selector.fmt}} /%}| No Selection | Single Select | Multi Select |
|---|---|---|
| `` | usd |
usd |
Allowed Children
Components Calendar Heatmap (`docs/components/calendar_heatmap.mdx`)
title: Calendar Heatmap keywords: ["calendar chart","activity chart","contribution graph"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% calendar_heatmap
data="demo.daily_orders"
date="date"
value="sum(total_sales)"
/%}Examples
Basic Usage
{% calendar_heatmap
data="demo.daily_orders"
date="date"
value="sum(total_sales)"
/%}Calendar Heatmap with Custom Colors
{% calendar_heatmap
data="demo.daily_orders"
date="date"
value="sum(total_sales)"
title="Daily Sales Heatmap"
chart_options={
color_scale = ["#0d0887", "#6300a7", "#a62098", "#d5546e", "#f68d45", "#fcd225", "#f0f921"]
}
/%}Calendar Heatmap with Conditional Colors
{% calendar_heatmap
data="demo.daily_orders"
date="date"
value="sum(total_sales)"
title="Sales Performance Heatmap"
chart_options={
conditional_colors = "case when sum(total_sales) > 65000 then '#22c55e' when sum(total_sales) > 40000 then '#f59e0b' else '#ef4444' end"
}
/%}Calendar Heatmap with Conditional Colors and Legend
{% calendar_heatmap
data="demo.daily_orders"
date="date"
value="sum(total_sales)"
title="Sales Performance Heatmap"
chart_options={
conditional_colors="case when sum(total_sales) > 65000 then '#22c55e' when sum(total_sales) > 40000 then '#f59e0b' else '#ef4444' end"
color_map={
"#22c55e"="High Sales"
"#f59e0b"="Medium Sales"
"#ef4444"="Low Sales"
}
}
/%}Attributes
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Example:
chart_options={
color_scale = ["value1", "value2"]
color_palette = ["value1", "value2"]
conditional_colors = "string"
color_map = {
"key1" = "value1"
"key2" = "value2"
}
}Attributes:
- color_scale:
array of strings- Array of hex colors for the heatmap gradient. A single-color array auto-expands to [background, color]. - color_palette:
array of strings- Deprecated. Usecolor_scale. - conditional_colors:
string- SQL CASE expression returning hex colors based on value - color_map:
map of key-value pairs- Maps hex colors to legend labels when using conditional_colors
Example:
echarts_options={
tooltip={ position="top" }
graphic=[{ type="text" }]
}Example:
echarts_series_options={
itemStyle={ borderRadius=8 }
}Components Callout (`docs/components/callout.mdx`)
title: Callout keywords: ["alert","banner","notice","admonition"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% callout type="info" title="Report Info"%}
This is an info callout
{% /callout %}Examples
Basic Usage
{% callout type="info" title="Report Info"%}
This is an info callout
{% /callout %}Attributes
Allowed values:
infosuccesswarningerror
Components Candlestick (`docs/components/candlestick.mdx`)
title: Candlestick keywords: ["ohlc","stock chart","financial chart"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
```sql stock_prices
SELECT '2024-01-01'::date as date, 100.0 as open, 105.0 as high, 95.0 as low, 102.0 as close
UNION ALL SELECT '2024-01-02'::date, 102.0, 108.0, 100.0, 105.0
UNION ALL SELECT '2024-01-03'::date, 105.0, 110.0, 103.0, 108.0
UNION ALL SELECT '2024-01-04'::date, 108.0, 112.0, 106.0, 104.0
UNION ALL SELECT '2024-01-05'::date, 104.0, 107.0, 98.0, 100.0
```
{% candlestick
data="stock_prices"
x="date"
open="open"
high="high"
low="low"
close="close"
y_axis_options={
min=90
max=115
}
/%}Examples
Basic Usage
```sql stock_prices
SELECT '2024-01-01'::date as date, 100.0 as open, 105.0 as high, 95.0 as low, 102.0 as close
UNION ALL SELECT '2024-01-02'::date, 102.0, 108.0, 100.0, 105.0
UNION ALL SELECT '2024-01-03'::date, 105.0, 110.0, 103.0, 108.0
UNION ALL SELECT '2024-01-04'::date, 108.0, 112.0, 106.0, 104.0
UNION ALL SELECT '2024-01-05'::date, 104.0, 107.0, 98.0, 100.0
```
{% candlestick
data="stock_prices"
x="date"
open="open"
high="high"
low="low"
close="close"
y_axis_options={
min=90
max=115
}
/%}With Volume
```sql stock_prices
SELECT '2024-01-01'::date as date, 100.0 as open, 105.0 as high, 95.0 as low, 102.0 as close, 1500000 as volume
UNION ALL SELECT '2024-01-02'::date, 102.0, 108.0, 100.0, 105.0, 2100000
UNION ALL SELECT '2024-01-03'::date, 105.0, 110.0, 103.0, 108.0, 1800000
UNION ALL SELECT '2024-01-04'::date, 108.0, 112.0, 106.0, 104.0, 2500000
UNION ALL SELECT '2024-01-05'::date, 104.0, 107.0, 98.0, 100.0, 3200000
```
{% candlestick
data="stock_prices"
x="date"
open="open"
high="high"
low="low"
close="close"
volume="volume"
y2_fmt="num0"
title="Stock Price with Volume"
y_axis_options={
min=90
max=115
}
/%}With Custom Colors
```sql stock_prices
SELECT '2024-01-01'::date as date, 100.0 as open, 105.0 as high, 95.0 as low, 102.0 as close
UNION ALL SELECT '2024-01-02'::date, 102.0, 108.0, 100.0, 105.0
UNION ALL SELECT '2024-01-03'::date, 105.0, 110.0, 103.0, 108.0
UNION ALL SELECT '2024-01-04'::date, 108.0, 112.0, 106.0, 104.0
UNION ALL SELECT '2024-01-05'::date, 104.0, 107.0, 98.0, 100.0
```
{% candlestick
data="stock_prices"
x="date"
open="open"
high="high"
low="low"
close="close"
chart_options={
up_color="#22c55e"
down_color="#ef4444"
}
y_axis_options={
min=90
max=115
}
/%}Attributes
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Allowed values:
dayweekmonthquarteryearhourday of weekday of monthday of yearweek of yearmonth of yearquarter of year
Example:
y_axis_options={
title = "string"
title_position = "top"
ticks = true
baseline = true
labels = true
gridlines = true
min = 0
max = 0
fit_to_data = true
interval = 0
}Attributes:
- title:
string - title_position:
string- Position of the axis title. "top" places it horizontally at the top, "side" places it vertically along the axis. Defaults to "side" for 100% stacked charts, "top" otherwise.- Allowed values:
topside
- Allowed values:
- ticks:
boolean - baseline:
boolean - labels:
boolean- Show/hide axis labels - gridlines:
boolean- Show/hide gridlines - min:
number- Minimum value for this axis (number for numeric axes, date string for date axes) - max:
number- Maximum value for this axis (number for numeric axes, date string for date axes) - fit_to_data:
boolean- Fit the axis to the data instead of including 0 - interval:
number- Interval between axis ticks for numeric axes. This option is a suggestion, the actual interval may differ.
Example:
x_axis_options={
title = "string"
show_title = true
label_wrap = true
ticks = true
baseline = true
labels = true
gridlines = true
min = 0
max = 0
fit_to_data = true
min_interval = "year"
max_interval = "year"
interval = 0
label_rotate = 0
title_arrow = true
max_label_length = 0
}Attributes:
- title:
string - show_title:
boolean- Whentrue, renders the auto-derived axis title (the x column name) below the chart. Ignored whentitleis set explicitly. Defaults tofalse— auto-derived column-name titles usually read as visual noise and the axis labels speak for themselves. - label_wrap:
boolean - ticks:
boolean - baseline:
boolean - labels:
boolean- Show/hide axis labels - gridlines:
boolean- Show/hide gridlines - min:
number- Minimum value for this axis (number for numeric axes, date string for date axes) - max:
number- Maximum value for this axis (number for numeric axes, date string for date axes) - fit_to_data:
boolean- Fit the axis to the data instead of including 0. Defaults to true for numeric x-axes, false otherwise. - min_interval:
string- Minimum interval between axis ticks for time-based axes. This option is a suggestion, the actual interval may differ.- Allowed values:
yearquartermonthweekdayhour
- Allowed values:
- max_interval:
string- Maximum interval between axis ticks for time-based axes. This option is a suggestion, the actual interval may differ.- Allowed values:
yearquartermonthweekdayhour
- Allowed values:
- interval:
number- Interval between axis ticks for numeric axes. This option is a suggestion, the actual interval may differ. - label_rotate:
number- Rotation angle of axis label in degrees. Positive values rotate clockwise, negative values rotate counter-clockwise. - title_arrow:
boolean- Show/hide the arrow (→) on the axis title - max_label_length:
number- Maximum character length for axis labels. Labels exceeding this length will be truncated with an ellipsis. Defaults to 20 characters when labels are rotated.
Attributes:
- up_color:
string- Color for bullish (up) candles - down_color:
string- Color for bearish (down) candles - zoom:
boolean- Enables zoom by dragging on the chart area
Example:
echarts_options={
tooltip={ position="top" }
graphic=[{ type="text" }]
}Components Chord Chart (`docs/components/chord_chart.mdx`)
title: Chord Chart keywords: ["chord diagram","circular flow","relationship diagram"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% chord_chart
data="demo.order_details"
source="category"
target="item_name"
value="sum(quantity)"
/%}Examples
Basic Usage
{% chord_chart
data="demo.order_details"
source="category"
target="item_name"
value="sum(quantity)"
/%}Chord Chart with Custom Colors
{% chord_chart
data="demo.order_details"
source="category"
target="item_name"
value="sum(quantity)"
title="Category to Item Flow"
chart_options={
color_palette = ["#0d0887", "#6300a7", "#a62098", "#d5546e", "#f68d45", "#fcd225", "#f0f921"]
}
/%}Chord Chart with Raw ECharts Overrides
{% chord_chart
data="demo.order_details"
source="category"
target="item_name"
value="sum(quantity)"
echarts_options={
series=[{ startAngle=0 padAngle=6 }]
}
echarts_series_options={
itemStyle={ borderRadius=6 }
}
/%}Attributes
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Example:
chart_options={
color_palette = ["value1", "value2"]
series_colors = {
"key1" = "value1"
"key2" = "value2"
}
}Attributes:
- color_palette:
array of strings - series_colors:
map of key-value pairs
Example:
echarts_options={
tooltip={ position="top" }
graphic=[{ type="text" }]
}Example:
echarts_series_options={
itemStyle={ borderRadius=8 }
}Components Clock (`docs/components/clock.mdx`)
title: Clock
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% clock /%}Examples
Default Clock (12h)
{% clock /%}24-Hour Clock
{% clock format="24h" /%}Attributes
Allowed values:
12h24h
Allowed values:
sansmono
Allowed values:
leftright
Components Combo Chart (`docs/components/combo_chart.mdx`)
title: Combo Chart keywords: ["mixed chart","combination chart","dual axis"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% combo_chart
data="demo.daily_orders"
x="date"
date_grain="month"
%}
{% line
y="sum(total_sales)"
/%}
{% bar
y="sum(transactions)"
axis="y2"
/%}
{% /combo_chart %}Examples
Basic Usage
{% combo_chart
data="demo.daily_orders"
x="date"
date_grain="month"
%}
{% line
y="sum(total_sales)"
/%}
{% bar
y="sum(transactions)"
axis="y2"
/%}
{% /combo_chart %}Combo Chart with Area and Line
{% combo_chart
data="demo.daily_orders"
x="date"
date_grain="month"
%}
{% area
y="sum(total_sales)"
/%}
{% line
y="avg(avg_transaction_value)"
axis="y2"
/%}
{% /combo_chart %}Combo Chart with Formatting
{% combo_chart
data="demo.daily_orders"
x="date"
y_fmt="usd"
y2_fmt="num0"
title="Sales and Transactions Overview"
subtitle="Combined view of sales and transaction volume"
date_grain="month"
%}
{% line
y="sum(total_sales)"
/%}
{% bar
y="sum(transactions)"
axis="y2"
/%}
{% /combo_chart %}Raw ECharts overrides
{% combo_chart
data="demo.daily_orders"
x="date"
date_grain="month"
echarts_options={
dataZoom=[{ type="slider" }]
tooltip={ position="top" }
}
echarts_series_options={
itemStyle={ borderRadius=4 }
}
%}
{% bar y="sum(total_sales)" /%}
{% /combo_chart %}Attributes
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Allowed values:
dayweekmonthquarteryearhourday of weekday of monthday of yearweek of yearmonth of yearquarter of year
Allowed values:
connectgapszero
Allowed values:
ascdescdata
Example:
y_axis_options={
title = "string"
title_position = "top"
ticks = true
baseline = true
labels = true
gridlines = true
min = 0
max = 0
fit_to_data = true
interval = 0
}Attributes:
- title:
string - title_position:
string- Position of the axis title. "top" places it horizontally at the top, "side" places it vertically along the axis. Defaults to "side" for 100% stacked charts, "top" otherwise.- Allowed values:
topside
- Allowed values:
- ticks:
boolean - baseline:
boolean - labels:
boolean- Show/hide axis labels - gridlines:
boolean- Show/hide gridlines - min:
number- Minimum value for this axis (number for numeric axes, date string for date axes) - max:
number- Maximum value for this axis (number for numeric axes, date string for date axes) - fit_to_data:
boolean- Fit the axis to the data instead of including 0 - interval:
number- Interval between axis ticks for numeric axes. This option is a suggestion, the actual interval may differ.
Example:
y2_axis_options={
title = "string"
title_position = "top"
ticks = true
baseline = true
labels = true
gridlines = true
min = 0
max = 0
fit_to_data = true
interval = 0
}Attributes:
- title:
string - title_position:
string- Position of the axis title. "top" places it horizontally at the top, "side" places it vertically along the axis. Defaults to "side" for 100% stacked charts, "top" otherwise.- Allowed values:
topside
- Allowed values:
- ticks:
boolean - baseline:
boolean - labels:
boolean- Show/hide axis labels - gridlines:
boolean- Show/hide gridlines - min:
number- Minimum value for this axis (number for numeric axes, date string for date axes) - max:
number- Maximum value for this axis (number for numeric axes, date string for date axes) - fit_to_data:
boolean- Fit the axis to the data instead of including 0 - interval:
number- Interval between axis ticks for numeric axes. This option is a suggestion, the actual interval may differ.
Example:
x_axis_options={
title = "string"
show_title = true
label_wrap = true
ticks = true
baseline = true
labels = true
gridlines = true
min = 0
max = 0
fit_to_data = true
min_interval = "year"
max_interval = "year"
interval = 0
label_rotate = 0
title_arrow = true
max_label_length = 0
}Attributes:
- title:
string - show_title:
boolean- Whentrue, renders the auto-derived axis title (the x column name) below the chart. Ignored whentitleis set explicitly. Defaults tofalse— auto-derived column-name titles usually read as visual noise and the axis labels speak for themselves. - label_wrap:
boolean - ticks:
boolean - baseline:
boolean - labels:
boolean- Show/hide axis labels - gridlines:
boolean- Show/hide gridlines - min:
number- Minimum value for this axis (number for numeric axes, date string for date axes) - max:
number- Maximum value for this axis (number for numeric axes, date string for date axes) - fit_to_data:
boolean- Fit the axis to the data instead of including 0. Defaults to true for numeric x-axes, false otherwise. - min_interval:
string- Minimum interval between axis ticks for time-based axes. This option is a suggestion, the actual interval may differ.- Allowed values:
yearquartermonthweekdayhour
- Allowed values:
- max_interval:
string- Maximum interval between axis ticks for time-based axes. This option is a suggestion, the actual interval may differ.- Allowed values:
yearquartermonthweekdayhour
- Allowed values:
- interval:
number- Interval between axis ticks for numeric axes. This option is a suggestion, the actual interval may differ. - label_rotate:
number- Rotation angle of axis label in degrees. Positive values rotate clockwise, negative values rotate counter-clockwise. - title_arrow:
boolean- Show/hide the arrow (→) on the axis title - max_label_length:
number- Maximum character length for axis labels. Labels exceeding this length will be truncated with an ellipsis. Defaults to 20 characters when labels are rotated.
Allowed values:
topbottom
Example:
chart_options={
color_palette = ["#3b82f6", "#8b5cf6", "#ec4899"]
series_colors = {
"Series A" = "#3b82f6"
"Series B" = "#10b981"
"Series C" = "#f59e0b"
}
zoom = true
top_padding = 0
}Attributes:
- color_palette:
array of strings- Array of hex color codes to use for series colors - series_colors:
map of key-value pairs- Map of series names to hex color codes for custom series coloring - zoom:
boolean- Enables zoom by dragging on the chart area - top_padding:
number- Additional padding (in px) above the chart area to prevent labels from being cut off
Example:
echarts_options={
tooltip={ position="top" }
dataZoom=[{ type="slider" }]
}Example:
echarts_series_options={
itemStyle={ borderRadius=8 }
markLine={ data=[{ type="average" }] }
}Allowed Children
Components Commentary (`docs/components/commentary.mdx`)
title: Commentary
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% commentary id="overview" /%}Examples
Page-wide commentary
{% commentary id="overview" /%}Reporting-period commentary
{% commentary id="monthly-review" scope="period" /%}Commentary per period and input
{% dropdown id="category" data="demo.daily_orders" value_column="category" select_first=true /%}
{% commentary id="category-review" scope=["period", "category"] /%}Attributes
Allowed values:
alwaysneverprint
Allowed values:
quotenormal
Components Comparison Selector (`docs/components/comparison_selector.mdx`)
title: Comparison Selector keywords: ["period over period","year over year","prior period","prior year","comparison","time comparison","date comparison","compare periods"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% comparison_selector
id="comp"
default_value="prior year"
/%}
{% big_value
data="demo.daily_orders"
value="sum(total_sales)"
fmt="usd1m"
date_range={
date="date"
range="last 12 months"
}
comparison={
compare_vs={{comp}}
}
/%}Examples
Using `comparison`
{% comparison_selector
id="comp"
default_value="prior year"
/%}
{% big_value
data="demo.daily_orders"
value="sum(total_sales)"
fmt="usd1m"
date_range={
date="date"
range="last 12 months"
}
comparison={
compare_vs={{comp}}
}
/%}Attributes
Allowed values:
prior yearprior periodtargetbenchmark
Using the Filter Variable
Reference this filter using {{filter_id}}. The value returned depends on where you use it.
| Context | Default Property | No Selection | Result |
|---|---|---|---|
| Inline SQL query | .comparison |
{"compare_vs": "prior year"} |
|
where attribute |
.comparison |
{"compare_vs": "prior year"} |
|
| Text / Markdown | .comparison |
{"compare_vs": "prior year"} |
Available Properties
You can also access specific properties using {{filter_id.property}}:
.selected
Returns the selected comparison value with quotes. Returns an empty string when no value is selected.
{% comparison_selector id="comparison" preset_values=["prior year", "prior period"] /%}
```sql sales_comparison
select
date,
sum(sales) as total_sales,
'{{comparison.selected}}' as comparison_type
from orders
group by date
```Example value: 'prior year'
.literal
Returns the raw unescaped selected value.
{% comparison_selector id="comparison" preset_values=["prior year", "prior period"] /%}
```sql sales_comparison
select
date,
sum(sales) as total_sales,
{{comparison.literal}} as comparison_type
from orders
group by date
```Example value: prior year
.compare_vs
Returns the comparison type.
Example value: prior year
.comparison
Returns the full comparison configuration as JSON. Use {{comp}} directly with compare_vs to dynamically configure comparisons.
{% comparison_selector id="comp" /%}
{% big_value
data="demo.daily_orders"
value="sum(total_sales)"
comparison={ compare_vs={{comp}} }
/%}Example value: {"compare_vs": "prior year"}
.agg
For benchmark comparisons: returns the aggregation function.
Example value: avg
.subject
For benchmark comparisons: returns the subject column that defines entities.
Example value: region
.target
For target comparisons: returns the target value or expression.
Example value: 100000
Allowed Children
Components Custom Echart (`docs/components/custom_echart.mdx`)
title: Custom Echart keywords: ["echarts","custom chart","advanced chart","escape hatch","config","waterfall","bridge"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% custom_echart data="demo.daily_orders" %}
{
xAxis: { type: 'category' },
yAxis: {},
series: [{ type: 'bar', encode: { x: 'category', y: 'total_sales' } }]
}
{% /custom_echart %}Examples
Basic Usage
{% custom_echart data="demo.daily_orders" %}
{
xAxis: { type: 'category' },
yAxis: {},
series: [{ type: 'bar', encode: { x: 'category', y: 'total_sales' } }]
}
{% /custom_echart %}Multiple Series with Tooltip
{% custom_echart data="demo.daily_orders" %}
{
tooltip: { trigger: 'axis' },
legend: { show: true },
xAxis: { type: 'category' },
yAxis: {},
series: [
{ type: 'bar', name: 'Sales', encode: { x: 'category', y: 'total_sales' } },
{ type: 'line', name: 'Transactions', encode: { x: 'category', y: 'transactions' } }
]
}
{% /custom_echart %}Dataset Transform
{% custom_echart data="demo.daily_orders" %}
{
dataset: [
{},
{ transform: { type: 'sort', config: { dimension: 'total_sales', order: 'desc' } } }
],
xAxis: { type: 'category' },
yAxis: {},
series: [{ type: 'bar', datasetIndex: 1, encode: { x: 'category', y: 'total_sales' } }]
}
{% /custom_echart %}Waterfall (EBITDA Bridge)
```sql ebitda_bridge
select 'Prior EBITDA' as step, 1 as ord, 0 as placeholder, null as inc, null as dec, 32000000 as tot
union all select 'Home', 2, 32000000, 6200000, null, null
union all select 'Sports', 3, 38200000, 4100000, null, null
union all select 'Groceries', 4, 40000000, null, 2300000, null
union all select 'Current EBITDA', 5, 0, null, null, 40000000
order by ord
```
{% custom_echart data="ebitda_bridge" title="EBITDA Bridge" %}
{
tooltip: { trigger: 'axis', valueFormatter: 'fmt:usd1m' },
xAxis: { type: 'category', axisLabel: { interval: 0 } },
yAxis: { axisLabel: { formatter: 'fmt:usd0m' } },
series: [
{ type: 'bar', stack: 'bridge', silent: true,
itemStyle: { color: 'transparent' },
encode: { x: 'step', y: 'placeholder' } },
{ type: 'bar', stack: 'bridge', name: 'Increase',
itemStyle: { color: '#22A39F' },
label: { show: true, position: 'top', formatter: 'fmt:usd1m' },
encode: { x: 'step', y: 'inc' } },
{ type: 'bar', stack: 'bridge', name: 'Decrease',
itemStyle: { color: '#E2483D' },
label: { show: true, position: 'bottom', formatter: 'fmt:usd1m' },
encode: { x: 'step', y: 'dec' } },
{ type: 'bar', stack: 'bridge', name: 'Total',
itemStyle: { color: '#475569' },
label: { show: true, position: 'top', formatter: 'fmt:usd1m' },
encode: { x: 'step', y: 'tot' } }
]
}
{% /custom_echart %}JavaScript mode (functions, gradients)
{% custom_echart data="demo.daily_orders" %}
{
tooltip: { trigger: 'axis', valueFormatter: (value) => fmt(value, 'usd0') },
xAxis: { type: 'category' },
yAxis: { axisLabel: { formatter: (value) => fmt(value, 'usd0m') } },
series: [
{
type: 'bar',
encode: { x: 'category', y: 'total_sales' },
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: theme.colorPalettes.default[0] },
{ offset: 1, color: theme.colorPalettes.default[1] }
])
},
label: { show: true, formatter: (params) => fmt(params.value.total_sales, 'usd0') }
}
]
}
{% /custom_echart %}Attributes
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Allowed values:
canvassvg
Components Custom Map (`docs/components/custom_map.mdx`)
title: Custom Map keywords: ["map","mapbox","maplibre","custom map","geospatial","gl"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% custom_map height=400 %}
// mapgl is Mapbox GL when a token is available, else MapLibre GL.
// container and the access token are provided for you.
const map = new mapgl.Map({
container,
center: [-71.06, 42.36],
zoom: 9
});
// Tiles load asynchronously — signal readiness once painted so PDF/PNG
// export captures a finished map rather than a blank frame.
map.on('idle', () => evidence.ready());
{% /custom_map %}Examples
Basic map
{% custom_map height=400 %}
// mapgl is Mapbox GL when a token is available, else MapLibre GL.
// container and the access token are provided for you.
const map = new mapgl.Map({
container,
center: [-71.06, 42.36],
zoom: 9
});
// Tiles load asynchronously — signal readiness once painted so PDF/PNG
// export captures a finished map rather than a blank frame.
map.on('idle', () => evidence.ready());
{% /custom_map %}Plot Evidence data as points
```sql locations
select 'New York' as name, -74.01 as lng, 40.71 as lat
union all select 'Los Angeles', -118.24, 34.05
union all select 'Chicago', -87.63, 41.88
union all select 'Houston', -95.37, 29.76
union all select 'Denver', -104.99, 39.74
union all select 'Seattle', -122.33, 47.61
union all select 'Miami', -80.19, 25.76
```
{% custom_map height=500 %}
const map = new mapgl.Map({ container, center: [-98, 39], zoom: 3 });
map.on('load', async () => {
const rows = await evidence.query('locations');
map.addSource('pts', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: rows.map((r) => ({
type: 'Feature',
geometry: { type: 'Point', coordinates: [r.lng, r.lat] },
properties: r
}))
}
});
map.addLayer({ id: 'pts', type: 'circle', source: 'pts', paint: { 'circle-radius': 5 } });
});
{% /custom_map %}Import a plugin (lasso / draw)
{% custom_map height=500 %}
// The draw control's buttons are CSS sprites — load its stylesheet or they render blank.
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'https://cdn.jsdelivr.net/npm/@mapbox/mapbox-gl-draw@1/dist/mapbox-gl-draw.css';
document.head.appendChild(link);
const { default: MapboxDraw } = await import('https://cdn.jsdelivr.net/npm/@mapbox/mapbox-gl-draw@1/+esm');
const map = new mapgl.Map({ container, center: [-98, 39], zoom: 3 });
map.on('load', () => {
map.addControl(new MapboxDraw({ displayControlsDefault: false, controls: { polygon: true, trash: true } }));
});
{% /custom_map %}How it works
The body of the tag is JavaScript that draws a map. It runs in a sandboxed iframe with three things already in scope:
container— the element to render the map into.mapgl— the map library (Mapbox GL, or MapLibre GL when there is no Mapbox token). Write standard Mapbox / MapLibre code against it.evidence— your page's data and state: run queries, read and write filters, and follow the report theme.
A minimal map:
const map = new mapgl.Map({ container, center: [-71.06, 42.36], zoom: 9 });
map.on('idle', () => evidence.ready());From there you write the same code you would anywhere else with Mapbox GL or MapLibre GL: load data with evidence.query, add sources and layers, and import any map plugin from a CDN. Because the code runs in an isolated iframe it cannot reach the rest of the page — it interacts with the report only through evidence. The full list of what is in scope is below.
Globals available to your code
The tag body runs as JavaScript inside an isolated iframe with these globals already in scope:
| Global | What it is |
|---|---|
container |
The element to mount your map into. |
mapgl |
The active map library — Mapbox GL when a token is available, otherwise MapLibre GL. Use new mapgl.Map({ container, ... }) for provider-agnostic code. |
maplibregl |
MapLibre GL, always available. |
mapboxgl |
Mapbox GL, with the access token already set. undefined when there is no token. |
provider / token |
The resolved provider and Mapbox token. |
evidence.query(name) |
Rows for a named query or sql block on the page. Returns a promise; page filters are already applied. |
evidence.variables |
Values passed via variables={…}. Always current; onVariablesChange fires on change. |
evidence.onVariablesChange(cb) |
Fires when any variables value changes. Returns an unsubscribe fn. |
evidence.filters |
get(), set(id, value), create(id, value, { column }), subscribe(cb) — read and write page filters. |
evidence.subscribe(cb) |
Fires on any state change (variables, filters, or theme). Returns unsubscribe. |
evidence.theme |
{ mode, palette } — resolved light/dark mode and categorical palette. |
evidence.onThemeChange(cb) |
Fires on theme/mode change — swap the basemap style here for dark mode. |
evidence.onResize(cb) |
Fires when the block resizes. Call map.resize() in the callback. |
evidence.onTeardown(cb) |
Register cleanup (e.g. () => map.remove()) run before the map re-renders on an edit. |
evidence.ready() |
Signal the first render is done, so PDF/PNG export captures a finished frame. |
Basemaps and keys
You don't need a Mapbox key in Studio. Maps render with a Mapbox basemap and the token is applied for you, so new mapboxgl.Map({ container }) works with no accessToken.
Where no token is configured — such as the open-source CLI or local development — maps use MapLibre GL with a free OpenFreeMap basemap instead. mapgl points at whichever library is active, so new mapgl.Map({ container }) works either way.
provider="maplibre"always uses the keyless MapLibre basemap.token="..."uses your own Mapbox account.
Choose a basemap style for the current theme with evidence.theme.mode:
- Mapbox —
mapbox://styles/mapbox/light-v11,mapbox://styles/mapbox/dark-v11 - MapLibre —
https://tiles.openfreemap.org/styles/positron(light),https://tiles.openfreemap.org/styles/dark
Switch the style when the report toggles light/dark:
evidence.onThemeChange((t) => map.setStyle(styleFor(t.mode)));Theming panels and overlays
Custom panels, legends, and controls you add to the map match the report theme with these CSS variables — set for you and updated on light/dark toggle (a sandboxed iframe has no host CSS otherwise, so use these rather than assuming a background):
var(--evidence-background)— surface backgroundvar(--evidence-foreground)— textvar(--evidence-muted-foreground)— secondary textvar(--evidence-border)— borders / dividers
evidence.theme.mode (light / dark) plus evidence.onThemeChange(cb) let you swap the basemap style to match dark mode.
Reacting to page filters and variables
A map and the rest of the page interact in three separate ways. Pick the one that matches what you need:
1. Filter the map's data by a page input (dropdown, date range). A filter takes effect where a query references it — passing it via variables= does not filter anything. Reference it in the SQL of the query the map runs, then evidence.query() that query:
-- city_sales (the query the map consumes)
select * from city_sales_all where {{ region_filter.filter }}const rows = await evidence.query('city_sales'); // re-runs with a region predicate when the dropdown changesSelecting the dropdown re-runs city_sales; call evidence.query again from evidence.subscribe / evidence.filters.subscribe to redraw with the filtered rows.
2. Read a filter/variable value in map JS (labels, client-side logic — not filtering). Pass it via variables= as a quoted {{ }} expression — use .literal for the raw value or .selected for a SQL-quoted value. This gives you the value in evidence.variables; it does not filter any query.
{% custom_map height=400 variables={ region="{{ region_filter.literal }}" } %}
const label = evidence.variables.region; // e.g. show the selected region in a badge
evidence.onVariablesChange((v) => { /* re-label */ });
{% /custom_map %}3. Write a filter back from the map (lasso/click → filter other components or a server re-query). Use evidence.filters and reference the filter in your query's SQL — the large-dataset pattern where you can't ship every row to the browser. A viewport-driven re-query on pan/zoom:
const map = new mapgl.Map({ container, center: [-98, 39], zoom: 4 });
async function load() {
const rows = await evidence.query('points_in_view'); // SQL references {{ min_lng.literal }} etc.
// ...update your source with rows...
}
map.on('load', load);
map.on('moveend', () => {
const b = map.getBounds();
evidence.filters.set('min_lng', b.getWest());
evidence.filters.set('max_lng', b.getEast());
evidence.filters.set('min_lat', b.getSouth());
evidence.filters.set('max_lat', b.getNorth());
});
evidence.filters.subscribe(load); // re-query when the bbox filters changeCall evidence.filters.create('min_lng', -130) once at startup for any filter the page's SQL references before the user interacts.
When you write a selection back as a list of ids, an empty list is ambiguous — it can mean "nothing selected yet" or "selected, matched nothing." Track selection state explicitly rather than checking the array length. And because id IN () is invalid SQL, write a value that matches no rows (e.g. ['__none__']) rather than an empty array when a selection matches nothing.
PDF and PNG export
Map tiles load after your setup code returns, so signal completion once the map has painted or export captures a blank frame:
map.on('idle', () => evidence.ready());PNG export (image download) reads the WebGL canvas, which requires preserveDrawingBuffer — custom_map turns it on for you, so image export works by default. (PDF export uses a page screenshot and needs nothing extra.) On a very heavy map you can trade image export for a small perf win by opting out:
const map = new mapgl.Map({ container, preserveDrawingBuffer: false }); // opt out: PNG blank, PDF still worksAdding plugins
Import any map-ecosystem library from a CDN — mapbox-gl-draw (lasso / box select), deck.gl, turf, h3-js, and so on. They load as normal dynamic imports. A plugin with its own UI (buttons, icons) also needs its stylesheet loaded, or its controls render blank:
// load the plugin's CSS too — icons are CSS sprites
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'https://cdn.jsdelivr.net/npm/@mapbox/mapbox-gl-draw@1/dist/mapbox-gl-draw.css';
document.head.appendChild(link);
const { default: MapboxDraw } = await import('https://cdn.jsdelivr.net/npm/@mapbox/mapbox-gl-draw@1/+esm');
map.addControl(new MapboxDraw());mapbox-gl-draw's controls appear only on the Mapbox provider; on MapLibre use @maplibre/maplibre-gl-draw.
Common issues
Blank map. If you passed a token, check it is valid — an invalid token still loads Mapbox but then fails to fetch tiles. Remove the token to fall back to the keyless MapLibre basemap.
Export shows a blank map. Tiles load after your setup code returns, so call evidence.ready() once the map has painted: map.on('idle', () => evidence.ready()).
Different basemap locally than when published. Local development and the CLI often have no Mapbox token, so they use MapLibre while Studio uses Mapbox. Set PUBLIC_MAPBOX_TOKEN locally to match, or set provider to pin one everywhere. If you rely on a Mapbox-only plugin, pin provider="mapbox".
Attributes
Allowed values:
mapboxmaplibre
Components Date Grain Selector (`docs/components/date_grain_selector.mdx`)
title: Date Grain Selector keywords: ["date grain","time grain","date bucket","date interval","date truncate","date grouping","time period","day week month year","date aggregation"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% date_grain_selector
id="time_grain"
default_value="month"
/%}
{% line_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
date_grain={{time_grain}}
/%}Examples
Using `date_grain`
{% date_grain_selector
id="time_grain"
default_value="month"
/%}
{% line_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
date_grain={{time_grain}}
/%}Using Inline SQL
{% date_grain_selector
id="time_grain"
default_value="month"
/%}
```sql sales_by_period
select
date_trunc({{time_grain}}, date) as period,
sum(total_sales) as total_sales
from demo.daily_orders
group by 1
order by 1
```
{% line_chart
data="sales_by_period"
x="period"
y="total_sales"
/%}Attributes
Allowed values:
dayweekmonthquarteryearhourday of weekday of monthday of yearweek of yearmonth of yearquarter of year
Using the Filter Variable
Reference this filter using {{filter_id}}. The value returned depends on where you use it.
| Context | Default Property | No Selection | Result |
|---|---|---|---|
| Inline SQL query | .selected |
'' |
'month' |
where attribute |
.selected |
'' |
'month' |
| Text / Markdown | .literal |
month |
Available Properties
You can also access specific properties using {{filter_id.property}}:
.selected
Returns the selected date grain value with quotes. Returns an empty string when no value is selected.
{% date_grain_selector id="grain" preset_values=["day", "week", "month"] /%}
```sql sales_by_grain
select
toStartOf{{grain.selected}}(date) as period,
sum(sales) as total_sales
from orders
group by period
```Example value: 'month'
.literal
Returns the raw unescaped selected value. Use this with the date_grain attribute.
{% date_grain_selector id="grain" preset_values=["day", "week", "month"] /%}
```sql sales_by_grain
select
{{grain.literal}} as period,
sum(sales) as total_sales
from orders
group by period
```Example value: month
Components Delta (`docs/components/delta.mdx`)
title: Delta keywords: ["change","difference","variance","comparison"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% delta
data="demo.daily_orders"
value="sum(total_sales)"
comparison={
compare_vs="target"
target="120000000"
}
/%}Examples
Basic Usage
{% delta
data="demo.daily_orders"
value="sum(total_sales)"
comparison={
compare_vs="target"
target="120000000"
}
/%}Semantic metric
{% delta metric="revenue" comparison={ compare_vs="prior year" } /%}Attributes
Example:
comparison={
compare_vs = "prior year"
display_type = "compared_value"
target = "string"
benchmark = {
agg = "avg"
subject = "store_name"
within = ["region"]
}
hide_pct = true
pct_fmt = "string"
abs_fmt = "string"
down_is_good = true
}Attributes:
- compare_vs:
string- Type of comparison to perform. Options: prior year (same period last year), prior period (previous period of same duration), target (compare against a target value), benchmark (compare against group average/aggregate)- Allowed values:
prior yearprior periodtargetbenchmark
- Allowed values:
- display_type:
string- What to display for comparison. Options: compared_value (comparison period value), abs (absolute change), pct (percentage change). Default: pct- Allowed values:
compared_valueabspct
- Allowed values:
- target:
string- Target value for target comparison. Can be a column name, aggregation (e.g., "sum(target_sales)"), or literal value. - benchmark:
options group- Options:
- agg:
string- Aggregation function to apply across benchmark group. Options: avg (average), median, min, max, sum, count, count_distinct- Allowed values:
avgmedianminmaxsumcountcount_distinct
- Allowed values:
- subject:
string- Column or expression that defines individual entities in the benchmark (e.g., "store_name", "customer_id"). Required for single-value components. - value:
string- Optional column or expression to use for benchmark calculation. If not specified, uses the main value column. Useful if you have a pre-aggregated benchmark table for RLS reasons. - within:
array of strings- Dimension columns to group the benchmark by (e.g., ["region"]). Leave empty for dataset-wide benchmark. - where:
string- SQL WHERE clause to filter which entities are included in the benchmark - exclude_self:
boolean- Exclude the current row from its own benchmark calculation (table context only). Default: false
- agg:
- Options:
- hide_pct:
boolean- Hide the percentage change line in comparison tooltips - pct_fmt:
string- Format code for percentage values in comparison tooltips - abs_fmt:
string- Format code for absolute values in comparison tooltips - down_is_good:
boolean- Whether a downward trend is considered positive. Defaults to the page'sdown_is_goodsetting.
Allowed values:
leftright
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Components Details (`docs/components/details.mdx`)
title: Details keywords: ["expandable","collapsible section","show more"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% details
title="Metric Definitions"
%}
**Sales:** includes sales of all core products in all regions
**Sales Growth:** YoY growth in sales
{% /details %}Examples
Basic Usage
{% details
title="Metric Definitions"
%}
**Sales:** includes sales of all core products in all regions
**Sales Growth:** YoY growth in sales
{% /details %}Open on Desktop, Collapsed on Mobile
{% details
title="Metric Definitions"
open=true
open_mobile=false
%}
**Sales:** includes sales of all core products in all regions
**Sales Growth:** YoY growth in sales
{% /details %}Attributes
Components Dimension (`docs/components/dimension.mdx`)
title: Dimension
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% table data="demo.daily_orders" %}
{% dimension value="category" /%}
{% dimension value="date" date_grain="month" title="Month" /%}
{% /table %}Examples
Table with Dimensions
{% table data="demo.daily_orders" %}
{% dimension value="category" /%}
{% dimension value="date" date_grain="month" title="Month" /%}
{% /table %}With Image
{% table data="demo.daily_orders" %}
{% dimension value="category" image="image_url" /%}
{% measure value="sum(total_sales)" /%}
{% /table %}With Link
{% table data="demo.daily_orders" %}
{% dimension value="category" link="concat('https://www.google.com/search?q=', category)" /%}
{% measure value="sum(total_sales)" /%}
{% /table %}With Logo
{% table data="sales" %}
{% dimension value="vendor" logo="domain" /%}
{% measure value="sum(amount)" /%}
{% /table %}Conditional Colors
{% table data="demo.daily_orders" %}
{% dimension value="category" conditional_colors="case when sum(total_sales) > 20000000 then '#22c55e' when sum(total_sales) > 10000000 then '#f59e0b' else '#ef4444' end" /%}
{% measure value="sum(total_sales)" fmt="usd1m" /%}
{% /table %}Attributes
Allowed values:
leftcenterright
Attributes:
- height:
number- Height of the image in pixels - width:
number- Width of the image in pixels - alt:
string- Alt text for the image - hide_label:
boolean- Whether to hide the text label and show only the image
Example:
logo_options={
size = "sm"
grayscale = true
hide_label = true
}Attributes:
- size:
string- Logo size: sm, base, lg, xl- Allowed values:
smbaselgxl
- Allowed values:
- grayscale:
boolean- Display logo in grayscale - hide_label:
boolean- Whether to hide the text label and show only the logo
Allowed values:
dayweekmonthquarteryearhourday of weekday of monthday of yearweek of yearmonth of yearquarter of year
Allowed values:
ascdesc
Allowed Parents
Components Dimension Grid (`docs/components/dimension_grid.mdx`)
title: Dimension Grid keywords: ["facet filter","dimension filter","multi-select grid"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% dimension_grid
id="product_filter"
data="demo.daily_orders"
/%}Examples
Basic Usage
{% dimension_grid
id="product_filter"
data="demo.daily_orders"
/%}With Explicit Dimensions and Metric
{% dimension_grid
id="sales_filter"
data="orders"
dimensions=["category", "region"]
metric="sum(sales)"
fmt="usd"
limit=5
/%}Using Filter in Query
{% dimension_grid id="order_filter" data="orders" /%}
```sql filtered_orders
select * from orders
where {{order_filter.filter}}
```Attributes
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Using the Filter Variable
Reference this filter using {{filter_id}}. The value returned depends on where you use it.
| Context | Default Property | No Selection | Result |
|---|
Available Properties
You can also access specific properties using {{filter_id.property}}:
.filter
Returns a complete SQL filter expression combining all dimension selections. Returns true when no values are selected.
{% dimension_grid id="dim_filter" data="orders" /%}
```sql filtered_orders
select * from orders
where {{dim_filter.filter}}
```.selected
Returns an object with dimension names as keys and selected values as arrays. Useful for accessing individual dimension selections.
{% dimension_grid id="dim_filter" data="orders" dimensions=["category", "region"] /%}
Selected categories: {{dim_filter.category}}
Selected regions: {{dim_filter.region}}.literal
Returns a human-readable summary of all selections.
{% dimension_grid id="dim_filter" data="orders" /%}
Active filters: {{dim_filter.literal}}Components Download (`docs/components/download.mdx`)
title: Download
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% download data="demo.daily_orders" /%}Examples
Basic Usage
{% download data="demo.daily_orders" /%}Custom Label and Filename
{% download
data="demo.daily_orders"
label="Export Orders"
filename="daily_orders_export"
/%}With Variant
{% download
data="demo.daily_orders"
label="Export Data"
variant="primary"
/%}With Large Limit
{% download
data="demo.daily_orders"
label="Download Recent Orders"
limit=50000
/%}Attributes
Allowed values:
defaultprimarydestructivesecondaryghostlink
Components Dropdown (`docs/components/dropdown.mdx`)
title: Dropdown keywords: ["cascading","linked","dependent","hierarchical","filter","select","relevant values"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% dropdown
id="category_filter"
data="demo.daily_orders"
value_column="category"
/%}
{% bar_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
filters=["category_filter"]
date_grain="month"
/%}Examples
Using `filters`
{% dropdown
id="category_filter"
data="demo.daily_orders"
value_column="category"
/%}
{% bar_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
filters=["category_filter"]
date_grain="month"
/%}Using `where`
{% dropdown
id="category_filter"
data="demo.daily_orders"
value_column="category"
/%}
{% bar_chart
data="demo.daily_orders"
x="date"
y="sum(total_sales)"
where="category = {{category_filter}}"
date_grain="month"
/%}Using Inline SQL
{% dropdown
id="category_filter"
data="demo.daily_orders"
value_column="category"
/%}
```sql filtered_orders
select * from demo.daily_orders
where category = {{category_filter}}
```
{% table data="filtered_orders" /%}Using `date_range`
{% dropdown
id="category_filter"
data="demo.daily_orders"
value_column="category"
date_range={
date="date"
range="last 12 months"
}
/%}Cascading Dropdowns (Linked Filters)
{% dropdown
id="category"
data="demo.order_details"
value_column="category"
title="Category"
/%}
{% dropdown
id="item"
data="demo.order_details"
value_column="item_name"
title="Item"
filters=["category"]
/%}
{% table
data="demo.order_details"
filters=["category", "item"]
/%}Attributes
Allowed values:
trending-uptrending-downclockcalendarcheckxinfoalert-circlehelp-circleeyeeye-offuseruserssettingscogplusminusupdownrightleftstarheartsearchfilefile-texthomemailfiltersharebelltrashcredit-cardglobekeycroissantmaprotaterewindbankreceiptactivitychart-columnchart-piechart-no-axes-combinedgoalrockettrophyapplecookiedonutbeefcakesouputensilsmilknutpyramidtrianglearrow-downarrow-leftarrow-rightarrow-upchevron-downchevron-leftchevron-rightchevron-upchevrons-downchevrons-leftchevrons-rightchevrons-upmenuexternal-linkcheck-circlex-circleedittrash-2copysavedownloaduploadsendrefreshredoundofolderfolder-openimagefile-imageuser-plususer-minususer-checklockunlocklog-inlog-outmessage-squaremessage-circlephonephone-callbell-offvideovideo-offplaypausestopskip-backskip-forwardvolumevolume-1volume-2volume-offvolume-xbookmarktaglinkunlinkshare-2alert-triangleloadermore-verticalmore-horizontalgridlistmaximizeminimizezoom-inzoom-outthumbs-upthumbs-downshopping-cartdollar-signcameraprintermonitorsmartphonelaptopcalculatorcloud-sun-rainsun-snowthermometer-sunthermometer-snowflakecloudycloud-rain-windcloud-rainwindsuncloud-snowthermometercloud-drizzlecloud-suncloudcloud-lightningsnowflakeflameatomfuelmagnetfactorytree-deciduouswaypointsplugdambattery
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Using the Filter Variable
Reference this filter using {{filter_id}}. The value returned depends on where you use it.
The examples below show values for three scenarios:
- No selection
- Single select: "Electronics" selected
- Multi select: "Sports" and "Home" selected (when
multiple=true)
| Context | Default Property | No Selection | Single Select | Multi Select |
|---|---|---|---|---|
| Inline SQL query | .selected |
'' |
'Electronics' |
('Sports', 'Home') |
where attribute |
.selected |
'' |
'Electronics' |
('Sports', 'Home') |
| Text / Markdown | .literal |
Electronics |
Sports, Home |
Available Properties
You can also access specific properties using {{filter_id.property}}:
.filter
Returns a complete SQL filter expression ready to use in WHERE clauses. Returns true when no value is selected.
{% dropdown id="category_filter" data="products" value_column="category" /%}
```sql filtered_products
select * from products
where {{category_filter.filter}}
```| No Selection | Single Select | Multi Select |
|---|---|---|
true |
category = 'Electronics' |
category IN ('Sports', 'Home') |
.selected
Returns the selected value(s) wrapped in quotes, suitable for SQL comparisons. Returns an empty string when no value is selected.
{% dropdown id="category_filter" data="products" value_column="category" /%}
```sql products_by_category
select * from products
where category = {{category_filter.selected}}
```| No Selection | Single Select | Multi Select |
|---|---|---|
'' |
'Electronics' |
('Sports', 'Home') |
.literal
Returns the raw unescaped selected value(s), useful for display in text or dynamic column selection.
{% dropdown id="sort_column" data="products" value_column="column_name" /%}
```sql dynamic_sort
select * from products
order by {{sort_column.literal}}
```| No Selection | Single Select | Multi Select |
|---|---|---|
| `` | Electronics |
Sports, Home |
.label
Returns the display label for the selected option(s). Falls back to the value if no label is defined.
{% dropdown id="category_filter" %}
{% option value="Electronics" label="Electronics" /%}
{% option value="Sports" label="Sports" /%}
{% option value="Home" label="Home" /%}
{% /dropdown %}
Selected: {{category_filter.label}}| No Selection | Single Select | Multi Select |
|---|---|---|
| `` | Electronics |
Sports, Home |
.fmt
Returns the format string associated with the selected option. For multiple selections, returns the first format.
{% dropdown id="metric_selector" %}
{% option value="revenue" label="Revenue" fmt="usd" /%}
{% option value="growth_rate" label="Growth Rate" fmt="pct1" /%}
{% /dropdown %}
{% big_value data={metrics} value=value fmt={{metric_selector.fmt}} /%}| No Selection | Single Select | Multi Select |
|---|---|---|
| `` | usd |
usd |
Allowed Children
Components Else (`docs/components/else.mdx`)
title: Else
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% if data="demo.daily_orders" %}
If Content
{% /if %}
{% else %}
Else Content
{% /else %}Examples
Basic Usage
{% if data="demo.daily_orders" %}
If Content
{% /if %}
{% else %}
Else Content
{% /else %}Attributes
This component has no attributes.
Components Else If (`docs/components/else_if.mdx`)
title: Else If
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% if data="demo.daily_orders" condition="no_rows" %}
If Content
{% /if %}
{% else_if data="demo.daily_orders" %}
Else If Content
{% /else_if %}Examples
Basic Usage
{% if data="demo.daily_orders" condition="no_rows" %}
If Content
{% /if %}
{% else_if data="demo.daily_orders" %}
Else If Content
{% /else_if %}Attributes
Allowed values:
no_rowshas_rows
Components Fence (`docs/components/fence.mdx`)
title: Fence
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
```language query_name
content
```Examples
Basic Usage
```language query_name
content
```Define a SQL query
```sql electronics_orders
SELECT * FROM demo.daily_orders WHERE category = 'Electronics'
```
{% table data="electronics_orders" /%}Attributes
Components File (`docs/components/file.mdx`)
title: File keywords: ["attachment","download","document","pdf","excel","csv","upload","asset"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% file
url="https://files.example.com/reports/q3-board-deck.pdf"
name="Q3 Board Deck.pdf"
date="2026-09-29"
/%}Examples
Basic Usage
{% file
url="https://files.example.com/reports/q3-board-deck.pdf"
name="Q3 Board Deck.pdf"
date="2026-09-29"
/%}Uploaded File
{% file
url="evd_org_01ABC/k7Xq2mB9.xlsx"
name="Regional Sales Model.xlsx"
date="2026-09-29"
/%}Attributes
Components Filter Bar (`docs/components/filter_bar.mdx`)
title: Filter Bar
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% filter_bar %}
...
{% /filter_bar %}Attributes
This component has no attributes.
Allowed Children
- dropdown
- table_filter
- date_grain_selector
- comparison_selector
- range_calendar
- button_group
- period_selector
Components Funnel Chart (`docs/components/funnel_chart.mdx`)
title: Funnel Chart keywords: ["funnel","conversion funnel","pipeline"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% funnel_chart
data="demo.daily_orders"
category="category"
value="sum(transactions)"
/%}Examples
Basic Usage
{% funnel_chart
data="demo.daily_orders"
category="category"
value="sum(transactions)"
/%}Funnel Chart with Custom Styling
{% funnel_chart
data="demo.daily_orders"
category="category"
value="sum(transactions)"
align="left"
label_position="outside"
gap=5
title="Styled Funnel"
/%}Funnel Chart with Custom Colors
{% funnel_chart
data="demo.daily_orders"
category="category"
value="sum(transactions)"
chart_options={
color_palette = ["#0d0887", "#6300a7", "#a62098", "#d5546e", "#f68d45", "#fcd225", "#f0f921"]
}
/%}Attributes
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Allowed values:
topbottom
Allowed values:
autoinsideoutsidecenter
Example:
chart_options={
color_palette = ["value1", "value2"]
}Attributes:
- color_palette:
array of strings
Allowed values:
centerleftright
Example:
echarts_options={
tooltip={ position="top" }
graphic=[{ type="text" }]
}Example:
echarts_series_options={
itemStyle={ borderRadius=8 }
}Components Heat Grid (`docs/components/heat_grid.mdx`)
title: Heat Grid keywords: ["metric grid","color grid","conditional formatting"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% heat_grid
data="appointments"
dimension="province"
value="avg(wait_time)"
thresholds=[60, 90]
units="MIN"
lower_is_better=true
/%}Examples
Basic Usage
{% heat_grid
data="appointments"
dimension="province"
value="avg(wait_time)"
thresholds=[60, 90]
units="MIN"
lower_is_better=true
/%}Attributes
Components Heatmap (`docs/components/heatmap.mdx`)
title: Heatmap keywords: ["heat map","matrix chart","color matrix"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% heatmap
data="demo.daily_orders"
x="date"
x_date_grain="year"
y="category"
value="sum(total_sales)"
/%}Examples
Basic Usage
{% heatmap
data="demo.daily_orders"
x="date"
x_date_grain="year"
y="category"
value="sum(total_sales)"
/%}With Custom Colors
{% heatmap
data="demo.daily_orders"
x="date"
x_date_grain="year"
y="category"
value="sum(total_sales)"
title="Sales Intensity by Year and Category"
chart_options={
color_scale = ["#f7fafc", "#e2e8f0", "#cbd5e0", "#a0aec0", "#718096", "#4a5568", "#2d3748"]
}
/%}Attributes
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Allowed values:
dayweekmonthquarteryearhourday of weekday of monthday of yearweek of yearmonth of yearquarter of year
Allowed values:
dayweekmonthquarteryearhourday of weekday of monthday of yearweek of yearmonth of yearquarter of year
Allowed values:
ascdesc
Allowed values:
ascdesc
Allowed values:
ascdesc
Example:
chart_options={
color_scale = ["value1", "value2"]
color_palette = ["value1", "value2"]
}Attributes:
- color_scale:
array of strings- Array of hex colors for the heatmap gradient. A single-color array auto-expands to [background, color]. - color_palette:
array of strings- Deprecated. Usecolor_scale.
Example:
echarts_options={
tooltip={ position="top" }
graphic=[{ type="text" }]
}Example:
echarts_series_options={
itemStyle={ borderRadius=8 }
}Components Heatmap Layer (`docs/components/heatmap_layer.mdx`)
title: Heatmap Layer
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% map %}
{% heatmap_layer
data="events"
lat="latitude"
lng="longitude"
/%}
{% /map %}Examples
Basic Heatmap
{% map %}
{% heatmap_layer
data="events"
lat="latitude"
lng="longitude"
/%}
{% /map %}Weighted Heatmap
{% map %}
{% heatmap_layer
data="sales"
lat="latitude"
lng="longitude"
weight="sum(revenue)"
/%}
{% /map %}Custom Color Palette
{% map %}
{% heatmap_layer
data="incidents"
lat="latitude"
lng="longitude"
color_scale=["#ffffb2", "#fecc5c", "#fd8d3c", "#f03b20", "#bd0026"]
/%}
{% /map %}Adjusted Radius and Intensity
{% map %}
{% heatmap_layer
data="visits"
lat="latitude"
lng="longitude"
weight="visit_count"
radius=50
intensity=2
/%}
{% /map %}Attributes
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Allowed Parents
Components Hidden In Print (`docs/components/hidden_in_print.mdx`)
title: Hidden In Print
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
Examples
Hidden in Print
{% hidden_in_print %}
This content is hidden when printed.
{% /hidden_in_print %}Attributes
This component has no attributes.
Components Histogram (`docs/components/histogram.mdx`)
title: Histogram keywords: ["distribution","frequency chart","frequency distribution"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% histogram
data="demo.daily_orders"
value="transactions"
bin_count=30
/%}Examples
Basic Usage
{% histogram
data="demo.daily_orders"
value="transactions"
bin_count=30
/%}Attributes
Example:
chart_options={
color_palette = ["value1", "value2"]
}Attributes:
- color_palette:
array of strings
Allowed values:
topbottom
Example:
echarts_options={
tooltip={ position="top" }
graphic=[{ type="text" }]
}Example:
echarts_series_options={
itemStyle={ borderRadius=8 }
}Components Horizontal Bar Chart (`docs/components/horizontal_bar_chart.mdx`)
title: Horizontal Bar Chart keywords: ["horizontal bar graph","row chart"]
{/* THIS FILE IS AUTOMATICALLY GENERATED - DO NOT MODIFY DIRECTLY */}
{% horizontal_bar_chart
data="demo.daily_orders"
y="category"
x="sum(total_sales)"
/%}Examples
Basic Usage
{% horizontal_bar_chart
data="demo.daily_orders"
y="category"
x="sum(total_sales)"
/%}Attributes
Example:
date_range={
range = "today"
date = "string"
}Attributes:
- range:
string- Time period to filter. Use presets like 'last 7 days', dynamic patterns like 'Last 90 days', custom ranges like '2020-01-01 to 2023-03-01', or partial ranges like 'from 2020-01-01'.- Allowed values:
todayyesterdaylast 7 dayslast 30 dayslast 3 monthslast 6 monthslast 12 monthsprevious weekprevious monthprevious quarterprevious yearthis weekthis monththis quarterthis yearnext weeknext monthnext quarternext yearweek to datemonth to datequarter to dateyear to dateall time
- Allowed values:
- date:
string- Date column to filter on. Required when the data has multiple date columns.
Allowed values:
dayweekmonthquarteryearhourday of weekday of monthday of yearweek of yearmonth of yearquarter of year
Allowed values:
connectgapszero
Example:
y_axis_options={
title = "string"
title_position = "top"
ticks = true
baseline = true
labels = true
gridlines = true
min = 0
max = 0
fit_to_data = true
interval = 0
}Attributes:
- title:
string - title_position:
string- Position of the axis title. "top" places it horizontally at the top, "side" places it vertically along the axis. Defaults to "side" for 100% stacked charts, "top" otherwise.- Allowed values:
topside
- Allowed values:
- ticks:
boolean - baseline:
boolean - labels:
boolean- Show/hide axis labels - gridlines:
boolean- Show/hide gridlines - min:
number- Minimum value for this axis (number for numeric axes, date string for date axes) - max:
number- Maximum value for this axis (number for numeric axes, date string for date axes) - fit_to_data:
boolean- Fit the axis to the data instead of including 0 - interval:
number- Interval between axis ticks for numeric axes. This option is a suggestion, the actual interval may differ.
Example:
x_axis_options={
title = "string"
show_title = true
label_wrap = true
ticks = true
baseline = true
labels = true
gridlines = true
min = 0
max = 0
fit_to_data = true
min_interval = "year"
max_interval = "year"
interval = 0
label_rotate = 0
title_arrow = true
max_label_length = 0
}Attributes:
- title:
string - show_title:
boolean- Whentrue, renders the auto-derived axis title (the x column name) below the chart. Ignored whentitleis set explici