TMR · Selected work

GDS — Designing a map platform in layers

I was the Lead Frontend Engineer on Map Core and Map Kit: two React packages designed for product teams to consume, from a reusable map experience to a GDS-connected one.

Map Core is the engine. A team brings its business data and composes the map experience it needs. Map Kit is the battery: it adds GDS data, defaults, branding, and operational tools while still allowing a product to include its own data.

Role
Lead Frontend Engineer
Started
January 2025
Focus
Frontend platform architecture and geospatial UX

01

Choose the package that matches the job

Map Core provides the reusable map UX. A product team passes one or more datasets through the Map Data Contract, then composes the pieces it needs around the GDSMap component: Layer Panel, Search Dock, Context Drawer, Tool Dock, and Map Controls.

Map Kit is the batteries-included path for teams that need GDS data. It builds on Map Core with Aurora styling, MapTiler basemaps, the default map composition, GDS data orchestration, and Path of Control. Products can use GDS datasets on their own or combine them with business datasets expressed through the same contract.

Map Core is the engine. Map Kit adds the battery for GDS-backed products.

02

The contract was the real platform

The Map Data Contract gives different products and services one way to describe points, lines, polygons, layers, relationships, metadata, and search behaviour.

I designed the Map Data Contract as the canonical frontend structure between product data and the renderer. It gives Map Core one vocabulary for entities, GeoJSON geometry, layer paths, styling, relationships, search configuration, and contextual lenses.

A Map Core consumer shapes its business data into that contract. Map Kit can fetch GDS feature types and turn the response into the same structure. Because GDSMap accepts one contract or an array, the engine can render several datasets through one interaction model.

03

How the pieces fit together

I kept the integration model explicit so teams could understand what each package supplied and where their product joined it.

  1. 01

    Map Core owns rendering

    It supplies spatial rendering, layers, basemaps, controls, search, contextual lenses, clustering, visual states, and interaction primitives.

  2. 02

    Map Kit owns operational composition

    It supplies the default GDS experience, Aurora theme, MapTiler basemaps, GDS feature-type loading, Path of Control, and the complete Map Core surface through one entry point.

  3. 03

    Products supply the business context

    A consuming team provides its datasets, authentication token, permissions, workflows, preferences, and any product-specific actions.

  4. 04

    GDS services supply map-ready data

    Map Kit requests visible feature types for the current viewport and parses the response into contracts that Map Core can render.

04

The map had to behave like an operational tool

The useful part of a map is not the basemap. It is the set of decisions a person can make over the data. I built a composable experience around a layer panel, search, a tool dock, map controls, and a context drawer.

The interaction model applied the same meaning to points, lines, and polygons: hover was a preview, focus meant inspection, selection was an explicit action, and related features stayed visually secondary. When states competed, selection won. That hierarchy made behaviour predictable across datasets instead of leaving each layer to invent its own cues.

The default Tool Dock includes Explore, Select, Select Area, Draw Polygon, and Route. Selection and drawing operate against the shared entity model; Route returns its start and end points to the consuming product. Map Kit adds Path of Control, which a lens can toggle to draw a dataset-coloured route over the map.

05

Performance belonged to the whole path

A map renderer cannot compensate for an application that sends every geometry it owns. I treated performance as a responsibility shared by services, transformations, the consumer, and Map Core.

Map Kit's data orchestrator requests visible feature types for the current bounding box. It respects minimum zoom levels, debounces viewport changes, cancels stale requests, and reuses a result when the bounding box has not changed. Map Core then renders the returned contracts and their interaction state.

Preferences followed the same philosophy. Map Core exposed a controlled model for view and layer state, while the application chose how to persist it.

06

Documentation was part of the package

A platform is not reusable if adoption begins with source-code archaeology. I made Storybook the working reference for Map Core and Map Kit, covering contracts, components, interaction states, workflows, and integration examples.

I backed that with architecture, integration, theming, accessibility, performance, deployment, and operator guides. Vitest covered package behaviour; Playwright and consumer smoke builds exercised the packages through the GDS web application and Storybook.

Both packages build to JavaScript, TypeScript declarations, and exported styles. They currently live in the GDS monorepo, with release workflows that build, test, pack, and verify them as consumable packages.

Looking back

The important decision was not choosing a map renderer. It was turning the map into an engine teams could consume, then adding a battery for teams that needed GDS data and conventions.

A product can start with Map Core and its own data, or start with Map Kit and get the GDS experience ready to extend. Both paths use the same contract and interaction model.

07

Technologies

Frontend

React, TypeScript, Vite

Mapping

MapLibre GL, React Map GL, GeoJSON, MapTiler

State and data

MobX, REST APIs

UI

Tailwind CSS, Radix UI

Testing and documentation

Vitest, Testing Library, Playwright, Storybook

Tooling

pnpm workspace, GitHub Actions