A React Native / Expo app that automatically detects nearby national parks, downloads their trail and boundary data for offline use, and supports NFC tag reading and writing via an ACR122U USB reader.
- Live park detection — uses device GPS to find the nearest NPS national park
- Offline trail maps — downloads trail GeoJSON + park boundary from OpenStreetMap/Overpass and NPS APIs; map tiles stay online via OSM
- Saved parks — browse and manage all downloaded parks; tap any to view its map
- NFC read — tap a programmed NFC tag to instantly load and download a park (requires NFC bridge running)
- NFC write — write any saved park to an NFC tag directly from the Saved screen
- Learn tab — structured wilderness survival lessons across 5 topics (navigation, first aid, wildlife, weather, Leave No Trace)
npm install# Web (opens in browser — easiest for development)
npm run web
# iOS simulator
npm run ios
# Android emulator
npm run android
# Or scan QR code with Expo Go
npm startThe NFC bridge is a local Node.js WebSocket server that talks to your ACR122U USB reader and relays card events to the app.
cd nfc-bridge
npm install
node index.jsThe bridge runs on ws://localhost:4242. Keep it running in a separate terminal while the app is open in a browser tab (web mode). The ⬡ NFC badge appears in the top-right corner of the map screen when the bridge is connected.
| Card type | Read | Write |
|---|---|---|
| NTAG213 / NTAG215 / NTAG216 | ✓ | ✓ |
| MIFARE Ultralight | ✓ | ✓ |
| MIFARE Classic 1K / 4K | ✓ | ✓ (block 4, default key A) |
NTAG213 sticker tags (cheap white NFC stickers on Amazon, ~$10 for 50) are the recommended choice. MIFARE Classic cards work but use a simpler raw-string format rather than NDEF.
- Open the app in the browser with the NFC bridge running
- Go to the Saved tab — a
⬡button appears next to each park when the bridge is connected - Tap
⬡next to a park → a modal appears asking you to tap a tag - Touch the NFC tag to the ACR122U reader — it gets written and the modal shows success
- Start the bridge and open the app
- Tap any written tag to the reader — the app immediately loads that park and starts downloading its trails
app/
(tabs)/
index.tsx # Map screen with live GPS and trail display
saved.tsx # Saved parks list with NFC write support
explore.tsx # Learn tab — topic and lesson browser
trail-finder.tsx # Nearest trail finder
topic/[slug].tsx # Topic lesson list screen
lesson/[id].tsx # Individual lesson screen
components/
TrailMap.tsx # Native WebView Leaflet map
TrailMap.web.tsx # Web Leaflet map
ParkInfoCard.tsx # Bottom card with park info and download button
AppHeader.tsx
hooks/
use-nearest-park.ts # GPS + NPS park lookup
use-background-download.ts # Boundary + trail download with progress
use-nfc-bridge.ts # WebSocket client for NFC read events
use-nfc-write.ts # WebSocket client for NFC write flow
services/
nps.ts # NPS API — park search and boundary fetch
trails.ts # Overpass API — trail GeoJSON fetch
trail-cache.ts/.web.ts # Trail data persistence (memory / localStorage)
boundary-cache.ts/.web.ts # Boundary persistence (memory / localStorage)
download-manager.ts/.web.ts # Downloaded park registry
data/
lessons.ts # All lesson content (5 topics, 22 lessons)
nfc-bridge/
index.js # ACR122U → WebSocket bridge
package.json
- Expo SDK 54 / React Native 0.81
- expo-location for GPS
- react-native-webview for the native map
- Leaflet embedded in the WebView for both native and web map rendering
- NPS API for park data and boundaries
- Overpass API for OSM trail data
- nfc-pcsc + ws for the NFC bridge (Node.js, runs separately on desktop)