Stockholm Weekend
Solo — product design, full-stack engineering, and deployment
Overview
Stockholm has a lot going on every weekend — exhibitions, concerts, food markets, family events — but the information is scattered across dozens of sites. I built Stockholm Weekend to fix that: a single map-first interface that aggregates events from VisitStockholm's API, lets you filter by category, save favourites, and generate a shareable weekend plan in seconds.
The app is live at sthlmweekend.com and syncs new events automatically every day via a scheduled GitHub Actions pipeline.
Live Product
The real app — browse events, switch to mobile view to see the gesture-driven interface.
The Challenge
The core product challenge was reducing friction: how do you go from "I want to do something this weekend" to a shareable plan in under a minute, without requiring an account? The technical challenge was building a reliable data pipeline that merges bilingual event data (English + Swedish APIs), deduplicates, and keeps the database fresh daily — all without manual intervention.
Technical Highlights
Map-first product design
The map is the homepage. Events are pins, categories are colour-coded, and the whole interface collapses into a bottom sheet on mobile. The design prioritises spatial discovery — you find events by where they are, not by scrolling a list.
No-account shareable plans
Users can save up to 10 events and generate a shareable plan URL in one tap — no login required. Plans are stored server-side with an edit token so the creator can delete them later. Rate limiting (20 plans/hour/IP) and IP hashing protect against abuse while preserving privacy.
Automated daily event sync
A GitHub Actions workflow runs at 04:00 UTC every day: it fetches both the English and Swedish VisitStockholm APIs, merges the bilingual data, filters for weekend-only events, downloads and stores images to Supabase Storage, and upserts everything to PostgreSQL via Prisma. The app always has fresh data without any manual work.
Gesture-driven mobile UX
The mobile experience is built around Framer Motion drag gestures: bottom sheets open with a spring animation, close with a downward swipe, and dismiss with a flick. Haptic feedback fires on key interactions. The filter and favourites drawers follow the same interaction model, making the whole app feel native on touch devices.
Bilingual from the data layer up
Every event has parallel English and Swedish fields, sourced from two separate API endpoints and merged by event ID. The UI language (next-intl) switches independently of the data language, stored in a cookie so the preference persists across sessions without polluting URLs.