Understanding Dimensions
The dimensions API lets you describe the meaningful axes of your data and have Data Navigator automatically build a navigable hierarchy. This page walks through the core concepts using small, concrete examples — all based on the same simple fruit-cost dataset from the Getting Started guide.
The Dataset
All examples on this page use this 4-item dataset:
const data = [
{ id: '_0', fruit: 'apple', store: 'a', cost: 3 },
{ id: '_1', fruit: 'banana', store: 'a', cost: 0.75 },
{ id: '_2', fruit: 'apple', store: 'b', cost: 2.75 },
{ id: '_3', fruit: 'banana', store: 'b', cost: 1.25 }
];1. Hand-Built List (No Dimensions)
The first chart in the Getting Started guide creates a flat linked list by hand — no dimensions API at all. Each node is manually defined with explicit edges:
const structure = {
nodes: {
_0: { id: '_0', data: { fruit: 'apple', store: 'a', cost: 3 }, edges: ['_0-_1', 'any-exit'] },
_1: { id: '_1', data: { fruit: 'banana', store: 'a', cost: 0.75 }, edges: ['_0-_1', '_1-_2', 'any-exit'] },
_2: { id: '_2', data: { fruit: 'apple', store: 'b', cost: 2.75 }, edges: ['_1-_2', '_2-_3', 'any-exit'] },
_3: { id: '_3', data: { fruit: 'banana', store: 'b', cost: 1.25 }, edges: ['_2-_3', 'any-exit'] }
},
edges: {
'_0-_1': { source: '_0', target: '_1', navigationRules: ['left', 'right'] },
'_1-_2': { source: '_1', target: '_2', navigationRules: ['left', 'right'] },
'_2-_3': { source: '_2', target: '_3', navigationRules: ['left', 'right'] },
'any-exit': { source: (d, c) => c, target: () => '', navigationRules: ['exit'] }
},
navigationRules: {
left: { key: 'ArrowLeft', direction: 'source' },
right: { key: 'ArrowRight', direction: 'target' },
exit: { key: 'Escape', direction: 'target' }
}
};This works, but you have to wire every node and edge yourself. The result is a simple chain: _0 ↔ _1 ↔ _2 ↔ _3. Navigate with ← / →.
Chart
Inspector
2. Two Categorical Dimensions (Store + Fruit)
Now let's use the dimensions API to make the same chart navigable with two dimensions: navigate by store with ← / → and by fruit with ↑ / ↓. This creates a dual hierarchy — dimension nodes at the top, division nodes underneath, and leaf data points at the bottom.
const structure = dataNavigator.structure({
data,
idKey: 'id',
dimensions: {
values: [
{
dimensionKey: 'store',
type: 'categorical',
behavior: { extents: 'terminal', childmostNavigation: 'across' }
},
{
dimensionKey: 'fruit',
type: 'categorical',
behavior: { extents: 'terminal', childmostNavigation: 'across' }
}
]
},
genericEdges: [
{
edgeId: 'any-exit',
edge: {
source: (_d, c) => c,
target: () => '',
navigationRules: ['exit']
}
}
]
});Drill down with Enter, drill up with W or J. At the leaf level, all four arrow keys work because both dimensions use childmostNavigation: 'across'.
Multiple navigable charts on one page
When you have several Data Navigator instances on the same page (especially with a shared dataset), each instance of Data Navigator must use unique ids. You may need to copy your datasets, to avoid sharing ids! If two structures share an id like _0, the rendered focus element from one chart can steal focus from another. This will throw users off, because they'll suddenly be moved to the wrong structure elsewhere when navigating. Either give each dataset its own id suffix (e.g. _0_a, _0_b) or make sure you remove all rendered elements when the user exits/blurs a chart (which is a less-accessible pattern than leaving the last-visited elements remaining rendered).
Chart
Inspector
3. Circular Extents
Change extents: 'terminal' to extents: 'circular' and navigation wraps around — moving right from store "b" loops back to store "a", and vice versa. Compare the inspector graph: notice the extra edges that connect the last division back to the first.
{
dimensionKey: 'store',
type: 'categorical',
behavior: { extents: 'circular', childmostNavigation: 'across' }
},
{
dimensionKey: 'fruit',
type: 'categorical',
behavior: { extents: 'circular', childmostNavigation: 'across' }
}Chart
Inspector
4. Numerical Dimension (Sorted by Cost)
Use the cost field as a numerical dimension. Data points are sorted by their numerical value — lowest cost first — and you navigate through them in order with ← / →.
{
dimensionKey: 'cost',
type: 'numerical',
behavior: { extents: 'terminal' }
}Since the numerical dimension uses terminal extents, navigation stops at the cheapest and most expensive items.
Chart
Inspector
5. Compressed Sparse Divisions
The compressSparseDivisions option merges divisions that would each contain only a single item into one combined division. This is useful when a dimension's grouping creates many near-empty buckets.
Here we use the dimensions API with a single store dimension plus compressSparseDivisions, which produces a structure similar to our original hand-built list — but generated automatically:
{
dimensionKey: 'store',
type: 'categorical',
behavior: { extents: 'terminal' },
operations: { compressSparseDivisions: true }
}Since each store has exactly 2 items (not 1), they won't be compressed in this case. But if we use fruit as the dimension instead — where "apple" has 2 items and "banana" has 2 items — the same applies. To see compression in action, imagine filtering down to a dataset where most divisions have only 1 item each. The API would merge them into a single flat list.
In this example we use a single cost numerical dimension with compressSparseDivisions: true. Since numerical dimensions with no subdivisions place all items in one division, compression has no visible effect here — but it demonstrates the option. The result is a flat sorted list of all 4 items by cost, navigable with ← / →.
{
dimensionKey: 'cost',
type: 'numerical',
behavior: { extents: 'terminal' },
operations: { compressSparseDivisions: true }
}Chart
Inspector
6. Level 0 Parent Node (Chart Description)
The parentOptions.addLevel0 option adds a root node above all dimension nodes. This is useful for giving a high-level description of the entire chart — similar to where you would put alt text for a visualization. The user lands on this node first, hears the chart description, then drills down into dimensions.
const structure = dataNavigator.structure({
data,
idKey: 'id',
dimensions: {
parentOptions: {
addLevel0: {
id: 'chart-root',
edges: [],
semantics: {
label: 'Stacked bar chart with 2 stores, each with 2 fruits. Press Enter to view dimensions or Escape to exit.'
}
}
},
values: [
{
dimensionKey: 'store',
type: 'categorical',
behavior: { extents: 'circular', childmostNavigation: 'across' }
},
{
dimensionKey: 'fruit',
type: 'categorical',
behavior: { extents: 'circular', childmostNavigation: 'across' }
}
]
},
genericEdges: [
{
edgeId: 'any-exit',
edge: {
source: (_d, c) => c,
target: () => '',
navigationRules: ['exit']
}
}
]
});When a user enters the chart, they first land on the chart-root node which announces "Stacked bar chart with 2 stores, each with 2 fruits. Press Enter to view dimensions or Escape to exit." From there, Enter drills into the dimension nodes, and the rest of navigation works like before.
Chart
Inspector
What's Next
Now that you understand the building blocks — categorical vs. numerical dimensions, terminal vs. circular extents, childmost navigation, compression, and Level 0 chart descriptions — see them applied to a real-world dataset in the Dimensions API Example.