On this page
Weather App
Fetch live weather data for any city and display temperature, conditions, and an icon. This project practices asynchronous JavaScript and working with third-party APIs.
Requirements
- JavaScript async/await and Fetch API
- Free OpenWeatherMap API key
- Basic HTML and CSS
Features
- Search by city name
- Display temperature (°C), humidity, wind speed, and weather description
- Show a weather icon from the API
- Handle loading, empty, and error states
Step 1: Get an API Key
- Sign up at openweathermap.org
- Navigate to API keys and generate a key
- New keys may take up to two hours to activate
Store your key in a variable — never commit it to a public repository.
Step 2: Project Structure
weather-app/
├── index.html
├── style.css
└── app.js
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Weather App</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="app">
<h1>Weather</h1>
<form id="search-form">
<input id="city-input" type="text" placeholder="Enter city..." required>
<button type="submit">Search</button>
</form>
<div id="status" class="hidden"></div>
<div id="weather" class="hidden">
<img id="icon" alt="Weather icon">
<h2 id="city-name"></h2>
<p id="description"></p>
<p id="temperature"></p>
<p id="details"></p>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
Step 3: Fetch Weather Data
const API_KEY = 'YOUR_API_KEY_HERE';
const BASE_URL = 'https://api.openweathermap.org/data/2.5/weather';
async function fetchWeather(city) {
const url = `${BASE_URL}?q=${encodeURIComponent(city)}&units=metric&appid=${API_KEY}`;
const response = await fetch(url);
if (!response.ok) {
if (response.status === 404) throw new Error('City not found');
throw new Error('Failed to fetch weather data');
}
return response.json();
}
Step 4: Render Results
function displayWeather(data) {
document.getElementById('city-name').textContent =
`${data.name}, ${data.sys.country}`;
document.getElementById('description').textContent =
data.weather[0].description;
document.getElementById('temperature').textContent =
`${Math.round(data.main.temp)}°C`;
document.getElementById('details').textContent =
`Humidity: ${data.main.humidity}% · Wind: ${data.wind.speed} m/s`;
document.getElementById('icon').src =
`https://openweathermap.org/img/wn/${data.weather[0].icon}@2x.png`;
}
Step 5: UI State Management
const statusEl = document.getElementById('status');
const weatherEl = document.getElementById('weather');
function showLoading() {
statusEl.textContent = 'Loading...';
statusEl.classList.remove('hidden');
weatherEl.classList.add('hidden');
}
function showError(message) {
statusEl.textContent = message;
statusEl.classList.remove('hidden');
weatherEl.classList.add('hidden');
}
function showWeather(data) {
statusEl.classList.add('hidden');
weatherEl.classList.remove('hidden');
displayWeather(data);
}
Step 6: Search Handler
document.getElementById('search-form').addEventListener('submit', async e => {
e.preventDefault();
const city = document.getElementById('city-input').value.trim();
if (!city) return;
showLoading();
try {
const data = await fetchWeather(city);
showWeather(data);
localStorage.setItem('lastCity', city);
} catch (err) {
showError(err.message);
}
});
// Load last searched city on page load
const lastCity = localStorage.getItem('lastCity');
if (lastCity) {
showLoading();
fetchWeather(lastCity).then(showWeather).catch(showError);
}
Step 7: Basic Styling
.app { max-width: 400px; margin: 2rem auto; text-align: center; font-family: system-ui, sans-serif; }
#search-form { display: flex; gap: 0.5rem; margin: 1rem 0; }
#search-form input { flex: 1; padding: 0.5rem; }
.hidden { display: none; }
#weather img { width: 100px; }
#temperature { font-size: 3rem; font-weight: bold; }
Extension Ideas
- 5-day forecast — use the
/forecastendpoint - Geolocation — detect the user’s city with
navigator.geolocation - Unit toggle — switch between °C and °F
- Recent searches — store and display the last five cities
- Dynamic backgrounds — change page color based on weather condition (sunny, rainy, cloudy)