[Guide] Build a Todo App - implementation notes
The Todo guide is a good example of the repo patterns applied to something small: a route shell, a global route store for cross-screen behavior, a screen document with stored derived state, and a Field that keeps DOM truth and state truth alive together so you can move between input postures without rewriting.
Do you actually store derived state like visibleItems, or do you compute it in render and rely on memoization? How do you decide when to commit input changes (keystroke vs blur vs submit) when you also want deterministic review surfaces? If filters are route state, how do you keep back/forward behavior predictable when the list is also mutating?
Comments (10)
Back to latestThe multiple sources of truth for Field is controversial, but it has saved us in migrations.
The guide is quietly consistent with the docs posture: store derived state so the UI renders instantly and predictably.
Related: Core Concepts (Best Practices) and Forms and Validation Best Practices.
We implemented the derived-state approach almost exactly and it made debugging much easier:
ts
function deriveVisible(state: any, visibility: 'all' | 'open' | 'done') {
const items = state.read('items');
const visible = visibility === 'done' ? items.filter((t: any) => t.done) :
visibility === 'open' ? items.filter((t: any) => !t.done) : items;
state.write('visibleItems', visible);
}
It is more writes, but it is a stable contract in the screen doc.
Counterpoint: storing derived lists can turn into a consistency trap if you do not re-derive on every mutation path.
We had a bug where the source list updated but the derived list did not because one action forgot to call derive.
Yes, that is the cost. The guide leans on render-visible effects to keep derivation on the happy path.
In practice we also centralize mutations and call derive in one place so you cannot forget it.
Centralizing mutation + derive makes it feel less fragile.
On commit timing: blur-to-commit worked for us because it made review surfaces stable without making typing feel laggy.
We still kept a final submit for anything that touched the network.
Back/forward got better when we treated filters as route truth and list mutations as document truth.
The route decides visibility; the document decides what exists.
A misunderstanding I had at first: I assumed storing derived state was a micro-optimization.
In this guide it is more about determinism and supportability than speed.
If you are using route state for filters, I would also render it as evidence in the shell.
It makes user bug reports much easier to interpret.
The guide is small, but it sets the tone: treat even a Todo app like a route contract, not a toy.
I would love to see an appendix on testing the derived state invariants. That is where we usually regress.