A location-based place recommendation app built with React, Leaflet.js and OpenStreetMap's Overpass API. Pick a mood β get real nearby places instantly.
Live Demo: https://nearby-vibes.vercel.app/
Pick a mood β Allow location β See real nearby places on an interactive map
| Mood | Places Found |
|---|---|
| π» Work Mode | CafΓ©s, Libraries, Coworking spaces |
| πΉ Date Night | Restaurants, Bars, Lounges |
| β‘ Quick Bite | Fast Food, Food Courts |
| πΈ Budget Hunt | Cheap Eats, Bakeries |
| π§ New in Town | All City Essentials |
- Interactive Leaflet map with custom numbered markers
- Route line drawn between your location and selected place
- One-click Open in Google Maps for walking/driving directions
- Auto-fit map bounds to show all results
- City Essentials Checklist - Hospital, Pharmacy, Police, ATM, Grocery, Transit, Post Office, Restaurant
- Tick off each essential as you find it with a progress bar
- Keyword Search - type anything like "SBI ATM" or "Apollo Hospital"
- Quick search suggestions built in
- Save any place to favourites - persisted in localStorage
- Recently visited history auto-recorded
- Stats Dashboard - total searches, mood breakdown, cities visited
- City Search - search any city worldwide, not just current location
- Auto-widen radius - if no results found, automatically retries at 5km then 10km
- Filter by Open Now, sort by Nearest or Top Rated
- π Dark / βοΈ Light mode toggle - preference saved
- π€ Share any place via WhatsApp or copy to clipboard
- π Custom emoji favicon
- Fully responsive - works on mobile and desktop
- Local language place names shown when available (Hindi, Tamil, Telugu)
| Category | Technology |
|---|---|
| Frontend | React 18, JavaScript ES6+ |
| Map | Leaflet.js, React-Leaflet |
| Map Tiles | OpenStreetMap |
| Places Data | Overpass API (OpenStreetMap) |
| Geocoding | Nominatim API |
| HTTP Client | Axios |
| Location | Browser Geolocation API |
| Storage | localStorage |
| Fonts | Syne, DM Sans (Google Fonts) |
π‘ Zero API cost - All APIs used are completely free with no key required.
nearby-recommender/
β
βββ public/
β βββ index.html
β βββ manifest.json
β
βββ src/
β βββ App.js # Main app - screens & state management
β βββ App.css # Global styles with dark/light theme
β βββ index.js # React entry point
β βββ index.css # Base reset
β β
β βββ components/
β β βββ MoodSelector.js/css # Home screen mood cards
β β βββ MapView.js/css # Leaflet map with markers & routes
β β βββ PlaceCard.js/css # Place card with fav/share/route actions
β β βββ SearchBar.js/css # Keyword search with suggestions
β β βββ EssentialsChecklist.js/css # New in Town checklist
β β βββ CitySearch.js/css # Search any city via Nominatim
β β βββ FavouritesPanel.js/css # Saved places panel
β β βββ StatsDashboard.js/css # Usage stats & history
β β
β βββ services/
β β βββ placesAPI.js # All Overpass & Nominatim API calls
β β
β βββ utils/
β βββ filters.js # Haversine distance, sort, star rating
β βββ storage.js # localStorage helpers for favs/stats
- Node.js 16+
- npm or yarn
# Clone the repo
git clone https://github.com/YOUR_USERNAME/nearby-vibes.git
# Navigate into project
cd nearby-vibes/nearby-recommender
# Install dependencies
npm install
# Start development server
npm startOpen http://localhost:3000 in your browser.
npm run buildUser picks a Mood
β
Browser Geolocation API β GPS coordinates
β
Overpass API query built dynamically
e.g. node["amenity"="cafe"](around:1500, lat, lon)
β
Raw OpenStreetMap nodes returned
β
Client-side mapping:
name Β· type Β· distance (Haversine) Β· rating Β· open status
β
Leaflet map renders with custom markers
+
Sidebar list with filter / sort
+
Detail panel on click
| Home Screen | Map View | New in Town |
|---|---|---|
![]() |
![]() |
![]() |
| Dark Mode | Stats Dashboard | Favourites |
|---|---|---|
![]() |
![]() |
![]() |
Used to calculate real-world distance between two GPS coordinates client-side:
function haversine(lat1, lon1, lat2, lon2) {
const R = 6371e3;
const toR = d => d * Math.PI / 180;
const dLat = toR(lat2 - lat1), dLon = toR(lon2 - lon1);
const a = Math.sin(dLat/2)**2 +
Math.cos(toR(lat1)) * Math.cos(toR(lat2)) * Math.sin(dLon/2)**2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
}Mood-based queries are built dynamically:
const query = `
[out:json][timeout:15];
(
node["amenity"="cafe"](around:${radius},${lat},${lon});
node["amenity"="library"](around:${radius},${lat},${lon});
);
out body 40;
`;If no results found, automatically retries with larger radius:
let results = await fetchPlaces(lat, lon, mood, 1500);
if (results.length === 0) results = await fetchPlaces(lat, lon, mood, 5000);
if (results.length === 0) results = await fetchPlaces(lat, lon, mood, 10000);| API | Purpose | Cost |
|---|---|---|
| Overpass API | Fetch real POI data from OpenStreetMap | Free |
| Nominatim | City geocoding & reverse geocoding | Free |
| OpenStreetMap Tiles | Map tile rendering | Free |
| Browser Geolocation API | User GPS coordinates | Built-in |
Pull requests are welcome! For major changes, please open an issue first.
MIT - feel free to use this as inspiration for your own portfolio. If you do, a star would be appreciated!
Built by Ankit Negi - CS undergraduate at IIITDM Kancheepuram
Built with β€οΈ using React & OpenStreetMap






