Drill-down
Tweak the number of levels
The widget is unopinionated about depth — you decide how many levels of selection state to keep, and append a slide for each one. Below is the same budget data shown with 2 to 5 levels of drill-down.
Levels:
3-level drill-down
Budget
$266 tracked
2024
2 levels
Categories → Transactions. Flat month, two columns of state.
3 levels
Months → Categories → Transactions. Adds time as a top-level pivot.
4 levels
Years → Months → Categories → Transactions. Useful for archives.
5 levels
Years → Months → Categories → Transactions → Line items. Receipt-level detail.
How the code scales
Each level is one piece of state and one conditional slide. To add a deeper level, add another useState and another ...(selected ? [slide] : []) block. The widget renders whatever array you give it.
// 5-level drill-down — one state hook per level
const [yearId, setYearId] = useState<string | null>(null);
const [monthId, setMonthId] = useState<string | null>(null);
const [catId, setCatId] = useState<string | null>(null);
const [txId, setTxId] = useState<string | null>(null);
const [itemId, setItemId] = useState<string | null>(null);
const onPrev = () => {
if (itemId) setItemId(null);
else if (txId) setTxId(null);
else if (catId) setCatId(null);
else if (monthId) setMonthId(null);
else if (yearId) setYearId(null);
};
const slides = [
{ key: "years", content: <YearList onPick={setYearId} /> },
...(year ? [{ key: "months", onBack: () => setYearId(null), content: <MonthList /> }] : []),
...(month ? [{ key: "cats", onBack: () => setMonthId(null), content: <CatList /> }] : []),
...(cat ? [{ key: "txs", onBack: () => setCatId(null), content: <TxList /> }] : []),
...(tx ? [{ key: "items", onBack: () => setTxId(null), content: <ItemList /> }] : []),
...(item ? [{ key: "detail", size: "2/3", onBack: () => setItemId(null), content: <Detail /> }] : []),
];
<MultiWidget
slides={slides}
hasSelection={yearId !== null}
onPrev={onPrev}
/>Tip: clear the deeper state when a parent changes (e.g. picking a new month should reset catId, txId, itemId) so the user never sees stale child slides.