TRIP PLANNER · iOS, WATCH & WEB
RidgeSync
RidgeSync is a backpacking trip planner for hikers. Plan multi-day routes on real trails, place camps, organise gear and food, and share the itinerary before you leave the trailhead. The web app is where you plan; the iOS app and its Watch companion are where you execute.
Open RidgeSync
- 3
- platforms: web, iPhone and Apple Watch
- 7
- trail datasets built from raw OpenStreetMap
- 100%
- of a planned trip works with no signal
- 120k
- lines of TypeScript
FEATURE BY FEATURE
What RidgeSync actually does

01
The trip library
Trips are the unit of the app, so the first screen is a library rather than a map: search, filter by favourites or downloaded or multi-day, and see difficulty, distance and estimated time before opening anything.
BUILT WITH
Supabase Postgres with row-level security. Each trip is a JSONB document alongside flat listing columns, so the list view reads cheap columns while the planner loads the full document. TanStack Query handles caching and invalidation.

02
Day-by-day planning
A multi-day route is not one line, it is a sequence of days you have to be honest about. Each day gets its own distance, elevation gain and moving time, its own elevation profile, and its own hourly weather, set against the route drawn over 3D terrain so the climbs are legible before you commit to them.
BUILT WITH
Valhalla for trail-snapped routing, Turf.js for geometry and splits, a rebuilt elevation dataset behind the profiles, and Mapbox GL terrain for the 3D view.

03
Download before you go
Downloads are a first-class screen, not a setting. A trip you have taken offline is marked as such and carries its own map pack, so the plan works in a valley with no bars.
BUILT WITH
expo-file-system for tile packs, expo-sqlite and MMKV for the trip and its stops, and an offline manifest in the shared kernel that both clients agree on.

04
On trail
Walking is where the information need inverts. Time, distance, elevation gained, distance remaining, pace, speed, and the one thing that actually matters mid-afternoon: how far to the next stop and when you will get there. GPS strength is shown because knowing the fix is weak changes how much you trust the rest.
BUILT WITH
expo-location with background task manager for tracking that survives a locked screen, running against the offline copy. The Watch companion is a native module speaking a small trip-brief protocol defined in the kernel.

05
The web planner
Where the actual work happens: the full route across 3D terrain, days and stats down the side, everything editable with a cursor. Plans made here are on the phone before you reach the trailhead.
BUILT WITH
React 19 and Vite with Mapbox GL JS, Tailwind and shadcn for the interface, consuming the same @trailplan/core kernel as the native app.
01Plan on a desk, execute on a trail
These are two different jobs and they wanted two different surfaces. Planning is a wide-screen, high-information activity: you are comparing routes, dragging camps, checking elevation against how far you actually want to walk on day three. Execution is the opposite: one glanceable number, gloves on, no signal, battery you care about.
So the web app is the planner and the iOS app is the execution surface, offline-first, with a Watch companion for the moments when taking a phone out is the wrong move.
02One kernel, three clients
Everything that is not a pixel lives in @trailplan/core: the trip schema, stats, food and packing maths, routing calls, weather, history, offline manifests, entitlements. It is deliberately platform-agnostic, with no browser globals and no Mapbox imports, so web, React Native and the Watch all consume the same logic instead of three drifting copies of it.
That boundary is enforced rather than hoped for. `npm run check:core` fails the build if anything platform-specific leaks into the kernel, which is the only way a rule like that survives contact with a deadline.
03Building the trail data
OpenStreetMap names segments, not hikes. “Hell Brook Cut Off” is a 0.4-mile connector that starts halfway up a mountain, hundreds of vertical feet from the nearest road. Publishing one hike per named OSM way, which is what the first version of the pipeline did, offers users a line nobody walks, starting where nobody parks.
So the model splits in two, the way AllTrails and HikerAtlas both split it: a segment database for tiles and custom routing, and separately-modelled routes as the thing people actually browse. Getting there meant building the datasets myself: hikes, campsites, waterfalls, viewpoints, water sources, even cell coverage, all as deterministic build steps from OSM, sharded and baked into vector tiles.
Trail-snapped routing runs through Valhalla, so a planned line follows real trail rather than crow-flies distance, with Overpass queries cached to keep the build honest and repeatable.
04Offline is the feature
A trip planner that needs signal is a trip planner that fails exactly when you need it. Map tile packs, the trip document, elevation and the day's stops all download ahead of time, tracked by an offline manifest in the kernel and stored in SQLite and MMKV on device.
Live tracking then runs against that local copy through background location tasks, which is also why the Watch app is worth having: the phone can stay in the pack.
05Built with
- TypeScript
- React 19
- Vite
- Expo
- Expo Router
- watchOS
- Supabase
- PostgreSQL
- Mapbox GL
- Valhalla
- Turf.js
- PMTiles
- TanStack Query
- Tailwind
- RevenueCat
- PostHog
// KEEP LOOKING



