tfl-components
London transport boards as copyable React source: a typed TfL client, a shadcn registry of status and arrivals UI, and a hosted Board that will run on an iPad in the kitchen.
Click to expandClick to collapse
tfl-components pipeline: TfL Unified API → tfl-ts types + hubs → Normalised rows → Data-aware board → Primitives + colours → iPad Board / your app
What it is
TfL publishes a free, rate-limited Unified API. The visual language of London transport is a different problem: official line colours, a trademarked roundel, Johnston-adjacent type, and operational quirks the JSON will not explain. tfl-components is that visual layer. You copy the React source into your app. tfl-ts supplies the typed data. The hosted Board at tfl.manglekuo.com turns any modern browser into a wall display if you do not want to ship an app.
What I built
Two repos that are meant to work together. tfl-ts is a zero-dependency TypeScript client generated from TfL's OpenAPI snapshot: friendly wrappers, 84 raw endpoints, static station topology, colours, and severity helpers. tfl-components is a shadcn registry, not an npm UI package, so consumers own the source. The docs site is the developer environment: live boards, Explorer, maps, and a Board builder whose config lives in the URL hash.
Why it was hard
The API will happily return the wrong StopPoint. Liverpool Street is several ids; poll the hub interchange and you get nothing. Circle, Hammersmith & City, and Metropolitan trains share metal and then do not, so a board that paints the raw lineId as gospel lies at Victoria and at Baker Street in different ways. Dark-mode Northern is brand black: a white fill would erase the identity, so contrast is a hard outline. Arrivals tiles are 48px and must never grow, while station names still have to be findable when they wrap or abbreviate.
Metrics
Registry v0.7.0 and tfl-ts 2.11.0 on npm, with about 300 weekly downloads. As of August 2026 the live site reported about 115 component installs.
▸Implementation notes
- Split the typed client from the React boards so an API package never drags in a UI stack, and so consumers copy source with shadcn instead of depending on a sealed component package.
- Organised the docs around developer intent (status, arrivals, maps, diagrams) rather than TfL's legacy endpoint taxonomy.
- Shipped STATION_HUBS in tfl-ts so boards resolve the sibling StopPoint that actually carries arrivals, instead of asking visitors to learn NaPTAN.
- Grouped Circle / Hammersmith & City / Metropolitan arrivals by shared-track topology. The raw lineId can flip along the same train.
- Locked arrivals to a 48px tile rhythm so boards stay aligned side by side. StationName still keeps find, copy, and screen readers on the full name when the paint abbreviates.
- Defaulted the roundel to a filled placeholder. Production builds stay silent; the official SVG is an explicit opt-in because the mark is trademarked.
