Architecture
Visivo runs in three phases (compile, run, and serve) that turn YAML into a live, interactive dashboard, with the heavy lifting split between a server that prepares data once and a browser that explores it instantly.
This page is the mental model for how those pieces fit together. For the worked example with real data, see How It Works; for the individual objects, start at the Concepts overview.
The three-phase model
compile builds the plan, run executes it, and serve ships the result to a browser. Each phase is a distinct CLI step, and visivo serve chains all three for you while watching your files.
flowchart LR
YAML[*.visivo.yml<br/>+ dbt™ models]:::input --> C1
subgraph C [compile]
direction TB
C1[Parse YAML]
C2[Build object DAG]
C3[Resolve refs ·<br/>write queries]
C1 --> C2 --> C3
end
subgraph R [run]
direction TB
R1[Execute job DAG]
R2[Query each source]
R3[Write Parquet<br/>per insight]
R1 --> R2 --> R3
end
subgraph S [serve]
direction TB
S1[Flask server]
S2[Serve the viewer]
S3[Hot reload on<br/>file change]
S1 --> S2 --> S3
end
C3 -->|target/project.json| R1
R3 -->|target/.../files/*.parquet| S1
classDef input fill:#eef2ff,stroke:#6366f1,color:#3730a3;
classDef phase fill:#f9f6f8,stroke:#713b57,color:#432334;
class C,R,S phase;
On a file change, serve re-runs the compile and run phases for the affected
objects, then hot-reloads the open dashboard.
-
Compile
Parses every
*.visivo.ymlfile, validates it against the JSON schema, builds the object DAG, and resolves${ref(...)}references into runnable SQL. No queries execute here. It writestarget/project.json(plusexplorer.jsonfor the lineage view). -
Run
Executes the job DAG: each insight's prepared query runs against its Source, and the result is cached as a Parquet file at
target/<run-id>/files/<insight>.parquet, alongside aninsights/<insight>.jsondescribing the client-side step. -
Serve
Starts a Flask server that serves the viewer and the run artifacts, then watches your project files. Saving a change triggers a re-compile and re-run, so the open dashboard hot-reloads with fresh data.
Three commands, or one
visivo compile, visivo run, and visivo serve can be run on their own,
but visivo serve runs all three for you. When it detects a file change it
re-compiles and re-runs the affected objects, then hot-reloads the open
dashboard. The same compile and run phases power
Visivo Cloud deploys.
The object DAG
Every Visivo project is a directed acyclic graph of objects whose dependencies flow in one direction: a Source feeds a Model, the semantic layer and Inputs enrich an Insight, and Insights are arranged by Charts into a Dashboard.
flowchart LR
SRC[Source]:::source --> MOD[Model]:::model
MOD --> INS[Insight]:::insight
SL[Semantic layer<br/>Metrics · Dimensions · Relations]:::metric --> INS
INP[Input]:::input --> INS
INS --> CHT[Chart]:::chart
CHT --> DSH[Dashboard]:::dashboard
classDef source fill:#fff7ed,stroke:#f97316,color:#9a3412;
classDef model fill:#fffbeb,stroke:#f59e0b,color:#92400e;
classDef metric fill:#ecfeff,stroke:#06b6d4,color:#155e75;
classDef insight fill:#faf5ff,stroke:#a855f7,color:#6b21a8;
classDef input fill:#eef2ff,stroke:#6366f1,color:#3730a3;
classDef chart fill:#fdf2f8,stroke:#ec4899,color:#9d174d;
classDef dashboard fill:#fff1f2,stroke:#f43f5e,color:#9f1239;
This is the same lineage Visivo computes during compile and renders in the Explorer. The object-type colors are fixed across the docs, the marketing site, and Visivo Cloud, so a Source always reads orange and a Dashboard always reads rose. Each object links down into its concept page and the generated configuration reference.
Insights, not traces
On the 2.0 line, an Insight is the unit of visualization. It binds a Model's columns to plotly props and carries its own interactions. A Chart is a container that arranges one or more Insights, and the same Insight can be reused across many Charts and Dashboards. Visivo computes the underlying data exactly once.
Insights: server prep, client interactivity
Visivo computes each insight's data once on the server, then does all filtering, splitting, and sorting in the browser, so interactions update instantly with no server round-trip.
flowchart LR
subgraph SERVER [Server · compile + run, once]
direction TB
SQ[Resolve insight<br/>into SQL]:::insight
EX[Execute against<br/>the Source]:::source
PQ[Cache result as<br/>Parquet]:::model
SQ --> EX --> PQ
end
subgraph BROWSER [Browser · the viewer]
direction TB
WASM[DuckDB-WASM<br/>loads the Parquet]:::metric
INT[Apply filter · split · sort<br/>client-side]:::input
PLOT[Render with plotly]:::chart
WASM --> INT --> PLOT
end
SERVER ==>|Parquet over HTTP| BROWSER
USER([Viewer changes a<br/>filter or input]):::dashboard -.->|no server call| INT
classDef source fill:#fff7ed,stroke:#f97316,color:#9a3412;
classDef model fill:#fffbeb,stroke:#f59e0b,color:#92400e;
classDef metric fill:#ecfeff,stroke:#06b6d4,color:#155e75;
classDef insight fill:#faf5ff,stroke:#a855f7,color:#6b21a8;
classDef input fill:#eef2ff,stroke:#6366f1,color:#3730a3;
classDef chart fill:#fdf2f8,stroke:#ec4899,color:#9d174d;
classDef dashboard fill:#fff1f2,stroke:#f43f5e,color:#9f1239;
The split keeps dashboards fast and cheap:
-
On the server (once)
During run, an insight's resolved query is executed against its Source and the result is written to a Parquet file. This is the only time your warehouse is touched for that insight, no matter how many people open the dashboard.
-
In the browser (every interaction)
The viewer ships DuckDB-WASM, which loads the Parquet file and runs the interactions (
filter,split, andsort) as local SQL. Changing an Input re-queries the in-browser data, with no call back to the server.
Where to go next
- Walk through the phases with real data in How It Works.
- Learn each object on its concept page.
- Ship a project to a shared URL with Visivo Cloud.