A modern weather dashboard built with Next.js 15 that lets you search for cities, compare their current conditions side-by-side, and explore hourly/daily forecasts in interactive charts — all powered by the free, key-less Open-Meteo API.
No API keys. No environment variables. Clone it and run it.
- City search — debounced, type-ahead search backed by Open-Meteo's geocoding API
- Multi-city comparison — add several cities and compare current conditions and forecasts side-by-side, persisted to
localStorage - Interactive charts — hourly temperature and precipitation probability rendered with Recharts (dual-axis composed chart)
- Dark / light mode — class-based theme toggle with no flash-of-unstyled-theme on load, persisted across reloads
- Fully responsive — single-column layout on mobile, multi-column comparison grid on larger screens
- Animated UI — card enter/exit and layout transitions via Framer Motion
- Robust data layer — every external API response is validated at runtime with Zod, so malformed or unexpected payloads fail loudly and predictably instead of corrupting the UI
- Loading & error states — skeleton loaders while fetching, inline error messages if Open-Meteo is unreachable
| Layer | Choice |
|---|---|
| Framework | Next.js 15 (App Router), TypeScript |
| Styling | Tailwind CSS v4 |
| Charts | Recharts |
| Animation | Framer Motion |
| Validation | Zod |
| Testing | Vitest, Testing Library |
| Weather data | Open-Meteo (geocoding + forecast APIs) |
| CI | GitHub Actions |
The browser never talks to Open-Meteo directly. Two Next.js Route Handlers act as a thin server-side proxy:
GET /api/geocode?q=<city>→ Open-Meteo geocoding APIGET /api/weather?latitude=<lat>&longitude=<lon>→ Open-Meteo forecast API
Both handlers validate the upstream JSON against Zod schemas (src/lib/schemas.ts) before returning it, so the rest of the app can trust the shape of the data it receives. This also gives the app a single place to normalize upstream errors into a consistent { error } response shape.
Client-side, a small set of hooks (src/hooks/) handle debounced search, AbortController-based request cancellation, localStorage-backed city persistence, and parallel per-city forecast fetching.
npm install
npm run devOpen http://localhost:3000. That's it — no .env file, no API key sign-up.
npm run dev # start the dev server
npm run build # production build
npm run start # run the production build
npm run lint # ESLint
npm run typecheck # tsc --noEmit
npm test # run the vitest suite once
npm run test:watch # vitest in watch modeThe test suite covers the Zod schemas, weather-code/flag/formatting utilities, both API route handlers (mocking the upstream fetch), and a UI component — running in CI on every push via GitHub Actions.
npm testOpen-Meteo is a free, open-source weather API that requires no API key or sign-up for non-commercial use, which keeps this project runnable by anyone who clones it with zero configuration.