Skip to content

Best Practices

Dashboard design and development best practices for dbt Charts.


Naming Conventions

Dashboard Names

  • Use lowercase with underscores: sales_overview, not Sales Overview
  • Be descriptive: monthly_sales_dashboard is better than dashboard1
  • Avoid spaces and special characters

Query Names

  • Use descriptive names that indicate what data the query fetches: sales, products, customers
  • Keep names concise but clear
  • Use explicit namespacing (queries.sales) when needed to avoid collisions

Chart IDs

  • Use descriptive names: revenue_by_month, not chart1
  • Indicate chart purpose: sales_trend, kpi_revenue
  • Be consistent with naming patterns

Variable Names

  • Use clear, descriptive names: region, date_range, min_revenue
  • Avoid abbreviations unless widely understood
  • Use snake_case consistently

Organization Strategies

Keep queries for the same domain together:

queries:
  # Sales queries
  sales: ...
  sales_by_region: ...

  # Product queries
  products: ...
  product_categories: ...

Use Tabs for Logical Grouping

Break dashboards into logical groups with tabs (or details for collapsible groups):

tabs:
  items:
    - title: "Overview"
      rows: [kpi_row]        # High-level KPIs
    - title: "Trends"
      rows: [trend_chart]    # Time series charts
    - title: "Details"
      rows: [detail_table]   # Detailed tables

Add Notes

notes: is available on every object, boards, charts, queries, variables, and layout items, and it's metadata for AI search and maintainer context, not end-user copy. It never appears in the rendered board, except at the board level, where Cloud surfaces it in the dashboard-card hover overlay on the project/home listing. The rule: if something displays, it has a display name (title, subtitle, label, text); if it doesn't display, it's notes.

title: "Sales Dashboard"
notes: "Monthly sales metrics and trends by region"

Performance Optimization

Limit Query Results

Add a LIMIT clause when you don't need all rows:

queries:
  sales:
    sql: |
      SELECT month, SUM(revenue) AS total_revenue
      FROM dundersign.orders
      GROUP BY month
      LIMIT 100  -- Only fetch 100 rows

Reuse Queries

Multiple charts can reference the same query (more efficient):

queries:
  sales:
    sql: |
      SELECT month, region, SUM(revenue) AS total_revenue
      FROM dundersign.orders
      GROUP BY month, region

charts:
  chart1:
    type: bar
    x: month
    y: total_revenue
    query: sales  # Same query
  chart2:
    type: line
    x: month
    y: total_revenue
    query: sales  # Same query

rows: [chart1, chart2]

Use Time Grain

Bucket timestamps to the grain you need directly in SQL:

queries:
  monthly:
    sql: |
      SELECT DATE_TRUNC('month', order_date) AS month, SUM(revenue) AS total_revenue
      FROM dundersign.orders
      GROUP BY 1

Filter Early

Filter in the query, not the chart:

queries:
  filtered:
    sql: |
      SELECT status, COUNT(*) AS ticket_count FROM dundersign.tickets
      WHERE {{ filter('status', status) }}
        AND {{ filter_date_range('created_at', date_range) }}
      GROUP BY status
    source: warehouse

User Experience

Provide Defaults

Set sensible default values for variables:

variables:
  region:
    default: "North"  # Show useful data by default

  date_range:
    input: daterange
    default: ["2024-01-01", "2024-01-31"]  # a daterange default is a [start, end] list

Use Appropriate Input Types

Match input types to use cases:

  • Select: Single choice from predefined list
  • Slider: Numeric ranges
  • Date range: Time period selection
  • Checkbox: Simple on/off flags

Add Notes

A chart-level notes: documents intent for AI search and tooling; it never renders:

charts:
  revenue_chart:
    title: "Revenue by Month"
    notes: "Monthly revenue trends over the past 12 months"
    query: sales
    type: bar

Test Interactions

Verify click actions and filters work as expected:

  • Test all variable combinations
  • Verify chart interactions
  • Check filter behavior
  • Test on different screen sizes

Common Patterns

Summary + Detail

Show high-level summary with drill-down detail using tabs:

tabs:
  items:
    - title: "Summary"
      rows: [summary_kpi]
    - title: "Details"
      rows: [detail_table]

For click-through drill-down instead of tabs, use chart-level link:; see Interactions.

Time Series with Filters

Combine time series with filters:

variables:
  date_range: ...

queries:
  trends:
    sql: |
      SELECT month, tickets_created FROM dundersign_serving.monthly_metrics
      WHERE {{ filter_date_range('month', date_range) }}
      ORDER BY month
    source: warehouse

charts:
  trend_chart:
    query: trends
    type: line
    x: month
    y: tickets_created

Comparison Views

Compare metrics side by side:

charts:
  comparison:
    query: sales
    type: bar
    x: month
    y: total_revenue
    color: region  # Compare regions

Anti-Patterns to Avoid

❌ Missing Query References

charts:
  chart1:
    type: bar
    x: month
    y: revenue
    query: nonexistent  # Error: query doesn't exist

❌ Missing Source

queries:
  sales:
    sql: SELECT * FROM orders  # Error: no source configured for this query

❌ Circular Dependencies

variables:
  var1:
    default: "{{ var2 }}"  # var2 depends on var1 - circular!
  var2:
    default: "{{ var1 }}"

❌ Missing Required Fields

charts:
  chart1:
    # Missing: query, type
    x: month

❌ YAML Syntax Errors

Common mistakes: - Wrong indentation (use spaces, not tabs) - Missing colons - Unquoted special characters


Version Control Best Practices

Commit Dashboard Files

Dashboard YAML files are version-controlled: - Commit dashboard changes with model changes - Use descriptive commit messages - Review dashboard changes in PRs

Organize by Feature

Group related dashboards:

charts/
  sales/
    overview.yml
    trends.yml
  marketing/
    campaigns.yml
    leads.yml

Document Changes

Add comments for complex logic:

queries:
  complex:
    sql: |
      SELECT COUNT(*) AS ticket_count FROM dundersign.tickets
      WHERE status = 'new'          -- open tickets only
        AND ticket_type = 'incident'
    source: warehouse

Development Workflow

Validate Before Committing

Always validate your dashboards before committing:

dct validate charts/

See the CLI Reference for validation options.

Inspect Resolved Output for Debugging

When troubleshooting, render the dashboard to JSON to see the resolved layout, charts, and executed query data:

dct render charts/sales.yml --format json

See the CLI Reference for rendering options.

Preview While Developing

Use the serve command for fast iteration:

dct serve

dct serve auto-discovers the project and prints the URL on startup. See the CLI Reference for server options.