An AI-powered travel planning app for Muslim-friendly trips from Malaysia. Built with Gemini AI. Personalised itineraries, flights, hotels, and daily plans in seconds.
Planning a halal-friendly trip is tedious. You juggle multiple tabs for flights, hotels, itineraries, and prayer-friendly venues — all while trying to keep within budget and account for your travel companions. Most trip planners are generic and ignore Muslim travel needs entirely.
Trip Planner fixes that. One app, your preferences, a full AI-generated trip plan in seconds.
| Who | What They Get |
|---|---|
| Solo Traveller | AI-generated itinerary tailored to solo budget and pace |
| Family / Group | Day-by-day plan with hotel options and activities suitable for the group size |
| Muslim Traveller | Religion-aware planning — halal context baked into the AI prompt |
Frontend — React Native, Expo Router, Expo Font, React Navigation, CalendarPicker
AI — Google Gemini 2.0 Flash via @google/generative-ai
Backend / Database — Firebase Firestore, Firebase Auth
Maps / Places — Google Places API (place search, photo retrieval)
Why this combo:
- Gemini 2.0 Flash gives structured JSON trip plans with a single prompt, no backend orchestration needed
- Expo Router handles file-based navigation cleanly across auth and tab flows
- Firebase handles auth and Firestore trip storage with minimal setup
- Google Places API fills in real photos for hotels and destinations dynamically
trip-planner/
├── app/
│ ├── _layout.jsx # Root layout, font loading, context provider
│ ├── index.tsx # Auth gate — redirects to mytrip or login
│ ├── (tabs)/
│ │ ├── mytrip.jsx # My Trips dashboard
│ │ ├── discover.jsx # Discover destinations
│ │ ├── history.jsx # Past trips
│ │ └── profile.jsx # User profile and edit
│ ├── auth/
│ │ ├── sign-in/ # Sign in screen
│ │ └── sign-up/ # Sign up screen
│ ├── create-trip/
│ │ ├── search-place.jsx # AI-powered place search
│ │ ├── select-traveler.jsx
│ │ ├── select-dates.jsx
│ │ ├── select-preferences.jsx
│ │ ├── select-budget.jsx
│ │ ├── review-trip.jsx
│ │ └── generate-trip.jsx # Calls Gemini, saves to Firestore
│ └── trip-details/
│ └── index.jsx # Full trip view with flights, hotels, day plan
│
├── components/
│ ├── CreateTrip/ # OptionCard, PlaceCard
│ ├── TripDetails/ # FlightInfo, HotelList, TripPlan
│ ├── MyTrips/ # UserTripList, UserTripCard, StartNewTripCard
│ ├── DiscoverPlaces/ # DiscoverCard
│ ├── LoadingModal.jsx
│ ├── ModalMessage.jsx
│ ├── NotificationMessage.jsx
│ └── Login.jsx
│
├── configs/
│ ├── AiModel.jsx # Gemini chat session setup
│ └── FirebaseConfig.js # Firebase init
│
├── constants/
│ ├── Colors.ts
│ └── Options.js # Traveler list, budget options, AI prompts
│
├── context/
│ └── CreateTripContext.js # Trip creation state across steps
│
└── utils/
├── googlePlaceUtils.jsx # Place ID, photo reference, photo URL helpers
└── tripUtils.js # Sort, filter, find upcoming trips
- Node.js 18+
- Expo CLI (
npm install -g expo-cli) - A Firebase project at console.firebase.google.com
- A Google Cloud project with Places API enabled
- A Google AI Studio API key at aistudio.google.com
git clone https://github.com/your-username/trip-planner.git
cd trip-planner- Create a new Firebase project
- Enable Authentication (Email/Password) and Firestore
- Copy your Firebase config from Project Settings
npm install
cp .env.example .env
# Fill in your keys (see Environment Variables below)npx expo start
# Scan QR with Expo Go, or press A for Android emulator.env
EXPO_PUBLIC_GOOGLE_GEMINI_API_KEY=
EXPO_PUBLIC_GOOGLE_PLACE_API_KEY=configs/FirebaseConfig.js — replace inline config values with your own:
const firebaseConfig = {
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: ""
};Do not commit
.envorFirebaseConfig.jswith real keys. Both are already in.gitignore.
| Feature | How It Works |
|---|---|
| AI Place Search | Gemini returns placeName, desc, continent for any search query |
| AI Trip Generation | Single prompt to Gemini returns full JSON — flights, hotels, daily itinerary |
| Muslim-Friendly Context | User religion pulled from Firestore profile and injected into the AI prompt |
| Google Places Photos | Real photos fetched dynamically for every hotel, destination, and activity card |
| Trip History | Trips filtered by end date — past trips go to History, upcoming stay in My Trips |
| Upcoming Trip Detection | Finds the nearest trip by start date, detects ongoing trips by date range |
| Delete Trip | Removes trip document from Firestore with confirmation modal |
Search Place → Select Traveler → Select Dates → Select Preference → Select Budget → Review → Generate
Each step writes to CreateTripContext. On Generate, the full context is interpolated into the AI prompt and sent to Gemini. The response is saved to Firestore under UserTrips/{docId}.
- Gemini's structured JSON output mode (
responseMimeType: "application/json") makes parsing AI responses reliable without extra sanitisation - Google Places photo pipeline (text search → place ID → photo reference → photo URL) requires three sequential API calls, worth caching
- Firebase Firestore with
wherequeries is sufficient for per-user trip storage at this scale, no need for a separate backend - Expo Router's file-based routing simplifies protected route handling via the
app/index.tsxauth gate pattern - Context across a multi-step form (CreateTripContext) is cleaner than prop-drilling or per-screen local state
- No offline support — trip generation requires a live connection to Gemini and Firebase
- Google Places photo pipeline is slow — three API calls per image with no caching layer
- No automated tests yet — Detox for E2E and Jest for unit tests are next
- AI prompt is not yet validated — malformed Gemini responses can break JSON parsing silently
- Profile photo upload is commented out — Cloudinary or Firebase Storage integration pending