A mobile-first web application for mapping memories with photos and videos. Built with Next.js, TypeScript, Tailwind CSS, Supabase, and Mapbox.
- 📍 Interactive Map: View all your memory pins on a beautiful map
- 📸 Media Support: Attach multiple photos and videos to each pin
- 🎬 Year Recap: Animated journey through your memories for any year
- 📱 Mobile-First: Optimized for mobile devices with responsive design
- 🎨 Modern UI: Clean, intuitive interface with smooth animations
- Next.js 16 (App Router) with TypeScript
- React 19 with TypeScript
- Tailwind CSS 4 for styling
- Supabase for database and storage
- Mapbox GL for interactive maps
npm install- Create a new project at supabase.com
- Go to SQL Editor and run the schema from
supabase/schema.sql - Go to Storage and create a new bucket named
user-media(make it public) - Copy your Supabase URL and anon key from Settings > API
- Enable Email Auth: Go to Authentication > Providers and ensure Email is enabled (it's enabled by default)
- Create an account at mapbox.com
- Get your access token from your account page
- Copy your Mapbox access token
Note: The Mapbox token is stored server-side only and proxied through a secure API route to prevent exposure in the client.
Create a .env.local file in the root directory:
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
MAPBOX_TOKEN=your_mapbox_access_tokenSecurity Note: MAPBOX_TOKEN (without NEXT_PUBLIC_) is server-side only and will never be exposed to the client. All Mapbox requests are proxied through /api/mapbox/* to keep your token secure.
npm run devOpen http://localhost:3000 in your browser.
The app uses Supabase's built-in authentication (auth.users) and two main tables:
- pins: Location pins with coordinates, title, description, and date (linked to
auth.users.id) - pin_media: Media files (photos/videos) associated with each pin
Row Level Security (RLS) is enabled - users can only see and manage their own pins. See supabase/schema.sql for the complete schema with RLS policies.
- Click the "+" button in the bottom-right corner
- Step 1: Choose location (use current location or enter coordinates)
- Step 2: Add details (title, description, date)
- Step 3: Upload photos/videos
- Click "Create Pin"
- Click any pin marker on the map
- View all media in a swipeable carousel
- See pin details and description
- Select a year from the dropdown in the top bar
- Click "Year Recap"
- Watch an animated journey through your memories
- Use controls to pause, skip, or navigate manually
├── app/
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Main map page
│ └── globals.css # Global styles
├── components/
│ ├── MapView.tsx # Mapbox map component
│ ├── AddPinButton.tsx # Floating action button
│ ├── AddPinModal.tsx # Multi-step pin creation form
│ ├── PinDetailsModal.tsx # Pin detail view
│ ├── MediaCarousel.tsx # Swipeable media carousel
│ ├── YearRecapControls.tsx # Year selector
│ └── YearRecapOverlay.tsx # Recap playback controls
├── lib/
│ └── supabase/
│ ├── client.ts # Supabase client (browser)
│ ├── server.ts # Supabase client (server)
│ └── queries.ts # Data access functions
├── types/
│ └── index.ts # TypeScript type definitions
└── supabase/
└── schema.sql # Database schema
- Authentication: Uses Supabase Auth - users must sign in/sign up to use the app
- Row Level Security: All data is protected by RLS policies - users can only access their own pins
- The app is optimized for mobile but works on desktop too
- All media is stored in Supabase Storage bucket
user-media - Security: Mapbox API token is stored server-side only and proxied through
/api/mapbox/*to prevent client-side exposure
- User authentication
- Pin editing and deletion
- Search and filter pins
- Export recap as video
- Share pins with others
- Multiple map styles
- Offline support
MIT