Build a Weather Dashboard with React and OpenWeather API

Build a Weather Dashboard with React and OpenWeather API

Build a Weather Dashboard with React and OpenWeather API

Connect React to a real-world API and build a polished UI with loading states, error handling, and local history.

Features

  • Current weather with temperature, humidity, wind
  • 5-day forecast with daily summary cards
  • City search with recent searches stored in localStorage
  • Celsius / Fahrenheit toggle
  • Responsive grid layout

Step 1 — API Hook

// hooks/useWeather.js
const API_KEY = import.meta.env.VITE_OPENWEATHER_KEY;
const BASE    = 'https://api.openweathermap.org/data/2.5';

export function useWeather(city) {
    const [weather,  setWeather]  = useState(null);
    const [forecast, setForecast] = useState([]);
    const [loading,  setLoading]  = useState(false);
    const [error,    setError]    = useState(null);

    useEffect(() => {
        if (!city) return;
        const controller = new AbortController();
        setLoading(true);
        setError(null);

        Promise.all([
            fetch(`${BASE}/weather?q=${city}&appid=${API_KEY}&units=metric`, { signal: controller.signal }),
            fetch(`${BASE}/forecast?q=${city}&appid=${API_KEY}&units=metric`, { signal: controller.signal }),
        ])
            .then(([r1, r2]) => Promise.all([r1.json(), r2.json()]))
            .then(([w, f]) => {
                if (w.cod !== 200) throw new Error(w.message);
                setWeather(w);
                setForecast(f.list.filter((_, i) => i % 8 === 0)); // one per day
            })
            .catch(err => { if (err.name !== 'AbortError') setError(err.message); })
            .finally(() => setLoading(false));

        return () => controller.abort();
    }, [city]);

    return { weather, forecast, loading, error };
}

Step 2 — Weather Card

function WeatherCard({ weather, unit }) {
    const temp = unit === 'C' ? weather.main.temp : (weather.main.temp * 9/5 + 32);

    return (
        <div className="card">
            <h2>{weather.name}, {weather.sys.country}</h2>
            <img src={`https://openweathermap.org/img/wn/${weather.weather[0].icon}@2x.png`} alt="" />
            <p className="temp">{Math.round(temp)}°{unit}</p>
            <p>{weather.weather[0].description}</p>
            <div className="details">
                <span>💧 {weather.main.humidity}%</span>
                <span>🌬 {weather.wind.speed} m/s</span>
                <span>👁 {weather.visibility / 1000} km</span>
            </div>
        </div>
    );
}
All Comments