Skip to main content

Quickstart for dbt Charts

dbt Charts
Quickstart
Beginner
Menu

    Introduction

    dbt Charts is a visualization layer that turns YAML files into interactive dashboards. You define queries, charts, and layouts as text and store them in Git alongside your dbt project. Because dbt Charts queries your dbt models directly, your dashboards stay in sync with the same transformations your team already trusts.

    You develop dbt Charts locally with the dct command-line tool: it reads a board file, runs its queries against the connection in your dbt profiles.yml, and serves the result in your browser with hot reload as you edit.

    In this quickstart, you'll learn how to:

    • Create the charts/ directory and a board file by hand.
    • Write your first five charts — a line, bar, area, pie, and table — from your dbt models.
    • Install dbt Charts and preview the board locally.

    This guide uses the fusion-jaffle-shop sample project, which is built for v1.12+ and includes marts such as order_items and location_performance. When you finish, you'll have a board that trends revenue over time, compares performance across store locations, and lists location-level key performance indicators (KPIs).

    Prerequisites

    • You have dbt v1.12+ installed. This guide assumes dbt v2.
      • If you're using v2, you must install the v1 adapter for your dbt Charts project to load in your browser. You don't have to do any additional configuration. For example, if you're using Snowflake, run pip install dbt-snowflake before installing dbt Charts. As development on dbt Charts continues, this prerequisite will eventually be removed.
    • You have Git and basic familiarity with the command line and YAML.
    • You have Python 3.9 or later and a package manager such as uv or pip, to install the dct tool.
    • You have a working connection to your data platform (Snowflake in this guide) that dbt can build into.

    Clone the sample project and build it

    dbt Charts queries tables that already exist in your data platform, so build the project's models before you chart them.

    1. Clone the sample project and change into it:

      git clone https://github.com/matthewshaver/jaffle-shop-multi-adapter.git
      cd fusion-jaffle-shop
    2. Add a profiles.yml for your data platform. The project's dbt_project.yml sets profile: default, so name your profile default and give it a dev target. dbt reads a profiles.yml in the project root, or falls back to ~/.dbt/profiles.yml. For platform-specific fields, refer to Connection profiles.

    3. Install the project's packages:

      dbt deps
    4. Seed the raw CSVs and build the models and tests in one command:

      dbt build

      The dbt build command runs seeds, models, snapshots, and tests in dependency order, so you don't need a separate dbt seed step. For the full list of commands, refer to dbt commands.

    5. Confirm that a mart the charts read returns rows:

      dbt show --inline "select count(*) from {{ ref('order_items') }}"
    note

    Record the database and schema that dbt built into — your chart SQL references them. To find them, run dbt list --output json and read each model's relation_name.

    Create the charts directory and board file

    The sample project doesn't ship any dbt Charts files, so you create them yourself. dbt Charts needs two things: a project config at the root that declares a data source, and at least one board file under charts/.

    1. From the project root, create the directory that holds your boards:

      mkdir charts

      Every .yaml, .yml, and .md file under charts/ becomes a served page, and nested directories become nested paths.

    2. Create dbt_charts.yml in the project root. Locally, dbt Charts reuses your dbt connection — there's no separate credential setup. Declare a source that points at a dbt profile and target, and boards reference it by name:

      dbt_charts.yml
      sources:
      analytics:
      type: dbt_profile
      profile: default
      target: dev

      The profile and target match your profiles.yml (default and dev in this project). Credentials stay in profiles.yml, including Snowflake key-pair auth through private_key_path if your account requires it.

    3. Create an empty board file at charts/analytics.yml and give it a title and a source:

      charts/analytics.yml
      title: "Jaffle Shop overview"
      source: analytics

    Over the next two steps, you add the queries and charts to this file, then the layout that positions them.

    Add your first five charts

    Now add five charts to charts/analytics.yml, one for each of the most common chart types. Each chart references a SQL query — defined in a queries: block — and maps that query's output columns to the chart's channels (x, y, theta, color).

    Each snippet below shows the query and the chart to add. Charts don't render until you add the layout in the next step, and the complete file appears in Lay out your board.

    Each query references its model by fully qualified location. Replace MY_DATABASE.MY_SCHEMA with the database and schema that dbt built your marts into. The SQL uses Snowflake syntax, so adjust functions like date_trunc if you build on another platform.

    1. Line chart: revenue over time

    Line charts show trends over time. This query aggregates revenue by month from the order_items model.

    queries:
    revenue_by_month:
    sql: |
    SELECT date_trunc('month', ordered_at) AS month,
    sum(product_price) AS revenue
    FROM MY_DATABASE.MY_SCHEMA.order_items
    GROUP BY 1
    ORDER BY 1

    charts:
    revenue_trend:
    type: line
    query: revenue_by_month
    x: month
    y: revenue
    title: "Revenue by month"

    2. Bar chart: revenue by location

    Bar charts compare a value across categories. This query reads pre-aggregated revenue per store from the location_performance mart.

    queries:
    revenue_by_location:
    sql: |
    SELECT location_name,
    total_revenue AS revenue
    FROM MY_DATABASE.MY_SCHEMA.location_performance
    ORDER BY total_revenue DESC

    charts:
    revenue_by_location:
    type: bar
    query: revenue_by_location
    x: location_name
    y: revenue
    title: "Revenue by location"

    3. Area chart: cumulative revenue

    Area charts emphasize accumulation over time. This query uses a window function to running-total monthly revenue.

    queries:
    cumulative_revenue:
    sql: |
    SELECT month,
    sum(revenue) OVER (ORDER BY month) AS cumulative_revenue
    FROM (
    SELECT date_trunc('month', ordered_at) AS month,
    sum(product_price) AS revenue
    FROM MY_DATABASE.MY_SCHEMA.order_items
    GROUP BY 1
    )
    ORDER BY month

    charts:
    cumulative_revenue:
    type: area
    query: cumulative_revenue
    x: month
    y: cumulative_revenue
    title: "Cumulative revenue"

    4. Pie chart: revenue by product type

    Pie (sector) charts show part-to-whole comparisons. This query splits revenue into food and drink. A sector chart uses theta for the wedge size and color for the category.

    queries:
    revenue_by_type:
    sql: |
    SELECT CASE WHEN is_food_item
    THEN 'Food' ELSE 'Drink' END AS product_type,
    sum(product_price) AS revenue
    FROM MY_DATABASE.MY_SCHEMA.order_items
    GROUP BY 1

    charts:
    revenue_by_type:
    type: pie
    query: revenue_by_type
    theta: revenue
    color: product_type
    title: "Revenue by product type"

    5. Table: location KPIs

    Tables show detailed values. This query lists several columns per location for a scannable KPI table.

    queries:
    location_kpis:
    sql: |
    SELECT location_name,
    total_revenue,
    total_orders,
    avg_order_value
    FROM MY_DATABASE.MY_SCHEMA.location_performance
    ORDER BY total_revenue DESC

    charts:
    location_kpis_table:
    type: table
    query: location_kpis
    title: "Location KPIs"

    Lay out your board

    Charts don't appear until you reference them in a layout. Boards use rows: to stack content vertically and cols: to place content side by side. Combine the queries and charts from the previous step under single queries: and charts: blocks, then add a layout that references each chart by its ID.

    Your complete charts/analytics.yml should now look like this, with MY_DATABASE.MY_SCHEMA replaced by the database and schema that dbt built your marts into:

    charts/analytics.yml
    title: "Jaffle Shop overview"
    source: analytics

    queries:
    revenue_by_month:
    sql: |
    SELECT date_trunc('month', ordered_at) AS month,
    sum(product_price) AS revenue
    FROM MY_DATABASE.MY_SCHEMA.order_items
    GROUP BY 1
    ORDER BY 1
    revenue_by_location:
    sql: |
    SELECT location_name,
    total_revenue AS revenue
    FROM MY_DATABASE.MY_SCHEMA.location_performance
    ORDER BY total_revenue DESC
    cumulative_revenue:
    sql: |
    SELECT month,
    sum(revenue) OVER (ORDER BY month) AS cumulative_revenue
    FROM (
    SELECT date_trunc('month', ordered_at) AS month,
    sum(product_price) AS revenue
    FROM MY_DATABASE.MY_SCHEMA.order_items
    GROUP BY 1
    )
    ORDER BY month
    revenue_by_type:
    sql: |
    SELECT CASE WHEN is_food_item
    THEN 'Food' ELSE 'Drink' END AS product_type,
    sum(product_price) AS revenue
    FROM MY_DATABASE.MY_SCHEMA.order_items
    GROUP BY 1
    location_kpis:
    sql: |
    SELECT location_name,
    total_revenue,
    total_orders,
    avg_order_value
    FROM MY_DATABASE.MY_SCHEMA.location_performance
    ORDER BY total_revenue DESC

    charts:
    revenue_trend:
    type: line
    query: revenue_by_month
    x: month
    y: revenue
    title: "Revenue by month"
    revenue_by_location:
    type: bar
    query: revenue_by_location
    x: location_name
    y: revenue
    title: "Revenue by location"
    cumulative_revenue:
    type: area
    query: cumulative_revenue
    x: month
    y: cumulative_revenue
    title: "Cumulative revenue"
    revenue_by_type:
    type: pie
    query: revenue_by_type
    theta: revenue
    color: product_type
    title: "Revenue by product type"
    location_kpis_table:
    type: table
    query: location_kpis
    title: "Location KPIs"

    rows:
    - title: "Trends"
    cols:
    - revenue_trend
    - cumulative_revenue
    - title: "Locations"
    cols:
    - revenue_by_location
    - revenue_by_type
    - title: "Detail"
    rows:
    - location_kpis_table

    Your project root should now contain:

    fusion-jaffle-shop/
    ├── charts/
    │ └── analytics.yml # Your board
    ├── dbt_charts.yml # dbt Charts project config
    ├── dbt_project.yml
    ├── models/
    ├── profiles.yml
    └── seeds/

    Install dbt Charts and view your board

    dbt Charts ships as a Python package with a command-line tool named dct.

    1. Install dbt Charts with uv:

      uv tool install dbt-charts

      Or, with pip:

      pip install dbt-charts
    2. From the project root, validate the board to catch YAML, reference, and SQL errors before it renders:

      dct validate

      With no arguments, dct validate checks every board under charts/. Pass a path — dct validate charts/analytics.yml — to check one.

    3. Start the local development server:

      dct serve

      dct walks up from the current directory to find dbt_charts.yml or dbt_project.yml, infers the SQL dialect from your profile target, and picks a port derived from the project directory.

    4. Open the URL dct prints. The page lists every board under charts/. Select analytics.yml to open yours.

      All five charts render across the three rows: the two trend charts side by side, the two location charts below them, and the KPI table at the bottom. The server hot-reloads as you edit YAML, so keep it running while you iterate.

    5. Optionally, render static output to share or to inspect the compiled SQL:

      dct render charts/analytics.yml --format html

    Troubleshooting

    • dbt_charts.yml validation error: extra inputs are not permitteddbt_charts.yml accepts a fixed set of top-level keys, such as sources and server. Remove anything else, including a name key.
    • A chart shows no data — Confirm your profiles.yml connection works (dbt debug) and that the profile's role can SELECT from the schemas your queries read.
    • Some charts show data but others are all zeros — Check whether the empty charts read a model that your dev target filters. A common pattern limits development data to a recent time window, such as a limit_in_dev macro on orders. On a static demo dataset, that window can exclude every row and zero out the models downstream of it, while models without the filter still populate. Anchor the window to the data's own max() timestamp, widen it, or build against a target that skips the filter.
    • Table or view not found — Confirm the database and schema prefix on each FROM matches where dbt actually builds the model. Run dbt list --output json and read relation_name to get the exact location.
    • Column not found — A chart's channels (x, y, theta, color) must match the column names your query's SELECT returns. Alias columns in SQL so the names line up.
    • A chart renders blank — Confirm the chart's type supports the channels you set. Sector charts (pie and donut) use theta and color, not x and y.
    • dct validate warns about re-aggregation — The cumulative revenue query sums an already-aggregated column on purpose, so you can ignore this warning. dct validate still exits successfully.

    What's next

    Congratulations 🎉! You built a dbt project locally, created your own dbt Charts directory and board file, wrote five charts from your dbt models, and previewed the board in your browser.

    From here, you can:

    • Add interactivity with variables so viewers can filter by location or date range.
    • Explore more chart types, such as small multiples, layered charts, and maps.
    • Add more files under charts/ — each one becomes its own page, so you can break a large dashboard into focused boards.
    • Chart additional dbt models by adding more SQL queries, or explore other query types such as values and HTTP sources.

    Was this page helpful?

    This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

    0