Find vegan restaurants near you on a live map β one Expo / React Native codebase running on Web, iOS and Android. Open source, privacy-first, and built to be self-hosted: spin up your own vegan map for your city, region or community.
Live at plantedmaps.com Β· built by Nyx & Fabian Β· part of Open Cosmic Software.
- π Privacy by design. Routing happens on your device β the server never sees where you are. There is no location tracking, no per-user analytics, no ad SDKs.
- π Radically transparent. A public, anonymous live dashboard at
/dashboardshows exactly what we count: a daily tally of searches and restaurant views. No IPs, no coordinates, no users, no sessions β thedaily_statstable literally has three columns:date, event, count. - π Real data. Live OpenStreetMap
diet:veganrestaurants via the Overpass API (no key needed). - π§© One codebase, three platforms. Web (Leaflet) and native
(
react-native-maps) behind one shared interface.
Accounts (JWT), favorites that sync across devices, a fully-vegan / vegan-options filter, "near me" geolocation, map β list view, restaurant detail pages, and crawlable per-restaurant SEO pages.
| Engine | |
|---|---|
| iOS / Android | react-native-maps (Apple Maps on iOS, Google Maps on Android) |
| Web | Leaflet + OpenStreetMap |
The map is a single component with platform-specific implementations
(components/AppMap.native.tsx / AppMap.web.tsx) behind one shared
interface β Metro resolves the right one per platform automatically.
- Expo SDK 56 + expo-router (file-based routing, tabs)
- TypeScript (strict)
- react-native-maps (native) / Leaflet (web)
- expo-location (geolocation), AsyncStorage (token)
- Overpass API for live
diet:veganrestaurant data (no key) - Backend: Express + better-sqlite3 + JWT (
server/)
cd server
npm install
cp .env.example .env # then edit .env β at minimum set JWT_SECRET
npm start # listens on $PORT (default 5050)The only thing you need is a JWT_SECRET (for accounts). Everything else in
.env.example is optional β geocoding (Stadia Maps), email verification
(Mailgun), Google Places enrichment, text-to-speech. The restaurant data
itself comes from the keyless Overpass API, so the map works out of the box.
The SQLite database is created automatically at DATABASE_PATH
(default ./data.db). Visit http://localhost:5050/dashboard to see your own
anonymous usage stats.
npm install
# point the app at your backend:
EXPO_PUBLIC_API_URL=http://localhost:5050 npm run web # Web
npm run ios # iOS (needs macOS / Xcode, or Expo Go)
npm run android # Android (needs Android Studio, or Expo Go)For Android native maps, add a Google Maps key to
app.json β android.config.googleMaps.apiKey.
Behind Caddy / nginx, route /api/*, /r/*, /dashboard and the
/sitemap*.xml paths to the backend; serve the built web app for everything
else.
| Method | Path | Notes |
|---|---|---|
POST |
/api/auth/register Β· /api/auth/login |
accounts |
GET |
/api/auth/me |
current user |
GET POST DELETE |
/api/favorites[/:id] |
saved spots (synced) |
GET POST DELETE |
/api/reviews[/:id] |
restaurant reviews |
GET |
/api/places?bbox=s,w,n,e&strict=true |
vegan restaurants in a bbox |
GET |
/api/stats |
anonymous aggregate counts |
GET |
/dashboard |
public privacy-first stats page |
GET |
/r/{en|de}/<slug> |
crawlable restaurant SEO page |
We count that things happen, never who or where. When you search the map, the bounding box is used to fetch results and then discarded β it is never stored. Routing is computed on your device. The only persistent analytics is a per-day, per-event integer. That's the whole point. π¦
MIT Β© 2026 Open Cosmic Software (Nyx & Fabian). See LICENSE.