BNBComps
Short-term rental comps UI: URL filters, pruned payloads, short-lived cache.
- Role
- Frontend engineering
- Type
- Analytics UI over HTTP APIs
- Client
- BNBComps
- Status
- Live

Outcome
Made comparable search usable under expensive aggregations by pruning list fields, debouncing, and caching identical filter hashes in the client.
Hosts and investors needed neighborhood-level STR comps. Existing tools were generic or too slow once filters stacked. Backend aggregations were expensive; the UI could not refetch a full payload on every toggle.
Filters in the URL where shareability mattered. Debounced API calls. Normalized DTOs so the table layer does not re-parse ad hoc JSON. Progressive detail: aggregates first, row expansion or a second fetch for deep comps.
- Frontend
- Next.js UI. Layout vs data hooks split so table logic stays testable.
- APIs
- Read-heavy JSON. Parameters for geography, bedrooms, date range. Owned by the product backend — this work consumed them.
- Databases
- No database owned in this engagement; analytics live behind the HTTP APIs.
- Multi-axis filters as query params; list endpoints return only columns the grid needs
- Comparable view composed from the same row model (no second fetching stack)
- Time-bucketed metrics when the API exposes them; empty/partial series handled in the client
- Saved filter sets in local persistence (not cross-device unless an account API exists)
- Stable keys / memoized selectors so re-filtering does not remount tables
Challenge
Heavy queries made the UI feel stuck.
Decision
Contract the list payload and cache by filter hash.
Implementation
Narrow fields, page/limit, debounce, in-memory or short-TTL cache keyed by the filter hash.
Result
Revisiting a recent view often skips a cold round trip. Freshness is the tradeoff.
Challenge
Raw metrics were hard to scan.
Decision
Lead with summaries; keep detail behind expansion.
Implementation
Headline aggregates first, then row-level comps on demand.
Result
Pricing decisions start from a readable summary instead of a dense grid dump.
Client cache keyed by filter hash
Users oscillate between two geographies. Identical requests should not hit expensive aggregations twice in a session.
Alternative. Always refetch, or a shared Redis cache on the API.
Tradeoff. Client cache is session-scoped. It does not help the first visitor or other devices.
URL-encoded filters
Shared links need to restore the same comparable view.
Alternative. Filters only in React state.
Tradeoff. Ugly URLs; worth it for shareability.
- BuiltNext.js comparable UI: filters, tables, progressive detail, client cache
- BuiltTyped DTOs and data hooks against the analytics HTTP API
- ExistingSTR analytics HTTP APIs and data pipeline behind them
- Frontend engineering engagement — not a claim of owning the analytics warehouse.
Comparable exploration stays responsive relative to uncached full payloads. No published conversion or revenue metrics.