Back to projects
2022Live

Weather App

7-day forecast as a progressive web app

ReactReduxWeather APIGeolocationPWA
View Live
Weather App screenshot

Could a Web App Feel Native?

PWAs had matured enough by 2022 that the gap between a web app and a native app was mostly perception. Offline support, home screen install, smooth animations — it was all there. I wanted to test whether you could build a weather app that someone would install on their phone and not know it wasn't from the App Store.

The visual reference was iOS Weather. Not to copy it, but to understand why it works — the way condition is communicated through color and motion, not just a text label. Rain looks like rain. Sun looks warm.

Three Things That Had to Work

OpenWeatherMap for the forecast data, the browser Geolocation API so it finds your location on first load, and a Service Worker so the last fetched forecast is available offline. Those three pieces had to work before anything else got built.

Redux handled the async data flow — fetching, loading states, error states, the current city, unit preferences. Each day of the 7-day forecast is its own slice of state, which kept the rendering logic straightforward.

Current + Forecast

The layout is two things: a large card for today with temperature, condition, feels-like, and humidity, then a horizontal scroll strip of the next seven days. City search and unit toggle live above the hero card.

01
Hero Card

Current temp, location, condition with an animated weather icon, feels-like and humidity

02
Forecast Strip

Horizontal scroll of 7 day cards — day name, condition icon, high/low

03
Search Bar

City search with recent searches from localStorage

04
Unit Toggle

C/F toggle, stored in preferences, updates all values immediately

05
PWA Install

Custom install prompt on first visit. Service worker caches the last successful fetch.

Under the Hood

Redux + Redux Thunk

Thunk async action creators call the OpenWeatherMap API. Loading and error states live in the weather slice alongside the forecast data.

Geolocation API

Requests location on load. Falls back gracefully when permission is denied — defaults to a city, doesn't break.

Service Worker

Caches the last successful API response. Stale-while-revalidate — you get the cached forecast instantly, then the fresh one loads in behind it.

CSS Animations

Weather condition animations (rain, sun, clouds) built entirely in CSS keyframes. No canvas, no JS animation library.

What Made It Hard

  • OpenWeatherMap's free tier rate-limits hard. Had to add request deduplication so rapid re-renders from Redux state changes didn't fire duplicate API calls.
  • Geolocation permission UX is inconsistent across browsers. Chrome, Firefox, Safari on iOS, Android WebView — each handles the prompt and the denied state differently. Tested all of them manually to get consistent fallback behavior.
  • Service worker cache versioning needs to be deliberate. An old cached response with yesterday's weather is worse than no response at all. Settled on a cache key that includes the date so stale forecasts get evicted automatically.