[Guide] Build a Map App (Middleware-First Defaults) - implementation notes
The Map App guide proposes a "middleware-first defaults" posture: decide fast-mode defaults before nested routes read context, use route context for shared decisions, and store derived "visible markers" so the list panel and map panel stay coherent. It's a strong take on making map UX predictable instead of timing-driven.
What did you actually put in middleware (defaults, normalization, capability checks) versus route code? How did you model route context so nested routes didn't read partial/unstable values? Did you store derived visible markers, and how did you prevent drift between list and map? What evidence did you render so a screenshot shows fast-mode/default decisions and viewport identity?
Comments (16)
Back to latestMiddleware-first defaults felt like the only way to avoid "the map loads, then changes its mind".
If defaults are decided after the UI renders, users perceive it as jitter.
We logged middleware decisions as contract lines and rendered the chosen posture as evidence:
txt
[tips] middleware=mapDefaults fastMode=true reason=capability:lowPower
[tips] routeContext viewport=city:sf zoom=12 reason=defaults
Once you can see the decision, debugging becomes straightforward.
Counterpoint: middleware can become a dumping ground and hide product decisions away from the UI team.
We kept middleware to normalization + capability defaults only. Everything else stayed in route code with visible evidence.
Yes. Middleware should decide *defaults*, not product semantics.
If a decision affects UX meaning, it should be in the contract and show up in evidence.
Route context was useful as a stable "map contract" so nested panels didn't re-decide things.
If each panel reads raw viewport independently, you get disagreement and the UI feels broken.
We stored derived visible markers (ids + summary counts) because list panel and map panel need the same truth.
If they don't agree, users lose trust fast ("why is it on the map but not in the list?").
We used a gate for expensive marker derive and we logged the gate decisions:
txt
[tips] gate=deriveMarkers allowed=false reason=pan:move remainingMs=120
[tips] gate=deriveMarkers allowed=true reason=debounce:fire viewportKey=vp_12Long-form argument: map apps feel unstable when the viewport identity isn't explicit.
We introduced a viewportKey and rendered it in debug mode. It made it obvious when the UI was rendering results for a different viewport than the user expects.
Counterpoint: storing derived markers can hide correctness issues if you don't re-derive reliably.
We centralized derive after any viewport write and made it idempotent so it can run often without fear.
We also rendered data-fast-mode, data-viewport-key, and data-marker-count as evidence.
Support screenshot triage became dramatically better.
Docs tie-in: the routing + architecture guidance is what makes middleware-first defaults feel coherent rather than hacky.
Related: Routing and Navigation and Architecture Guides.
We treated fast mode as a posture with explicit tradeoffs (less precision, fewer updates).
If you don't label the tradeoff, users think the app is buggy.
Counterpoint: middleware can also make local dev confusing because behavior differs between environments.
We fixed it by logging the middleware decisions in dev and rendering them in debug mode.
A trick: keep list panel ordering derived from visible marker ids so it can't drift.
If list fetches separately from map, you'll always have inconsistency.
Long-form counterpoint: middleware-first defaults can become an excuse to avoid building good UI affordances (toggles, settings).
Defaults are important, but user choice still matters. The key is making both explicit in the contract.
The guide made me treat map UX as a contract, not an emergent property of effects and fetch timing.
Once defaults and context are explicit, everything else gets calmer.
If you're implementing this, start by logging middleware decisions and rendering the chosen posture as evidence.
Those two things force you into a coherent model quickly.