Grouping, spanners and pivots

Column groups

Add column groups when several columns share a common category, like four quarters under one revenue header, and a single flat header row would be confusing.

What you can do

  • A ColumnGroup requires label and takes either columns (an array of field names) or nested columnGroups, never both at once.
  • Nesting columnGroups inside a ColumnGroup renders an extra spanner header row above the leaf group’s own header, one level per level of nesting.
  • Columns inside one group should stay contiguous in the columns array; a group spanning non-adjacent columns renders incorrectly.
  • columnGroups lives on TableSpec alongside columns; every field a group references still needs its own entry in columns.
  • There is no limit on how many ColumnGroup entries or nesting levels TableSpec.columnGroups can hold.

Set on TableSpec.columnGroups on a table panel; a chart:* panel has no multi-row header to group, since it has no columns.

Spec

View spec
{
  "id": "quarterly-revenue-table",
  "type": "table",
  "title": "Revenue by quarter",
  "data": {
    "rows": [
      {
        "segment": "Enterprise",
        "q1": 210000,
        "q2": 230000,
        "q3": 250000,
        "q4": 270000
      },
      {
        "segment": "SMB",
        "q1": 90000,
        "q2": 95000,
        "q3": 101000,
        "q4": 108000
      }
    ]
  },
  "spec": {
    "columns": [
      {
        "field": "segment",
        "label": "Segment"
      },
      {
        "field": "q1",
        "label": "Q1",
        "format": {
          "type": "currency",
          "currency": "USD",
          "compact": true
        },
        "align": "right"
      },
      {
        "field": "q2",
        "label": "Q2",
        "format": {
          "type": "currency",
          "currency": "USD",
          "compact": true
        },
        "align": "right"
      },
      {
        "field": "q3",
        "label": "Q3",
        "format": {
          "type": "currency",
          "currency": "USD",
          "compact": true
        },
        "align": "right"
      },
      {
        "field": "q4",
        "label": "Q4",
        "format": {
          "type": "currency",
          "currency": "USD",
          "compact": true
        },
        "align": "right"
      }
    ],
    "columnGroups": [
      {
        "label": "Revenue by quarter",
        "columnGroups": [
          {
            "label": "H1",
            "columns": [
              "q1",
              "q2"
            ]
          },
          {
            "label": "H2",
            "columns": [
              "q3",
              "q4"
            ]
          }
        ]
      }
    ]
  }
}

The outer group "Revenue by quarter" has no columns of its own; it nests two child groups, "H1" and "H2", each with its own contiguous columns pair, producing two spanner header rows above the leaf headers.

Try it live

Open the collections-report template in the Builder

Open the collections-report template, panel cl-ledger for three columnGroups alongside bullet, sparkline, bar, winloss, and icon cells.

Build it in dvt

Every table capability on this site is a few lines of the same declarative JSON spec. Read the full spec format, or connect an AI agent to your warehouse and build one live in the quickstart.

Follow the build

dvt is in founding research. Leave your email and we'll reach out when there's something to see — no newsletters, no noise.

No spam. We use this to reach out directly, nothing else.