Building a weather app is one of the best beginner JavaScript projects. It teaches you about DOM manipulation, fetch API, async/await, and CSS styling — all in one project.
In this tutorial, we'll build a complete weather app using only vanilla JavaScript. No React, no Vue, no API keys required.
What We're Building
A weather application that:
- Shows current weather for any city
- Displays temperature, humidity, wind speed, and conditions
- Has a clean, responsive design
- Works offline with cached data
Step 1: The HTML
Start with a simple structure:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Weather App</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="weather-app">
<h1>Weather App</h1>
<div class="search">
<input type="text" id="cityInput" placeholder="Enter city name...">
<button id="searchBtn">Search</button>
</div>
<div class="weather-card" id="weatherCard" style="display:none;">
<h2 id="cityName"></h2>
<div class="temp" id="temperature"></div>
<div class="condition" id="condition"></div>
<div class="details">
<span>Humidity: <strong id="humidity"></strong></span>
<span>Wind: <strong id="wind"></strong></span>
</div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
Step 2: The CSS
Style it with a modern dark theme:
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: system-ui, sans-serif;
min-height: 100vh;
background: linear-gradient(135deg, #0d0d0d, #1a1a2e);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
.weather-app {
text-align: center;
padding: 40px;
}
.search {
display: flex;
gap: 8px;
margin: 24px 0;
justify-content: center;
}
.search input {
padding: 12px 20px;
border-radius: 10px;
border: 1px solid #333;
background: #141414;
color: #fff;
font-size: 16px;
width: 280px;
}
.search button {
padding: 12px 24px;
border-radius: 10px;
border: none;
background: #e8e8e8;
color: #111;
font-weight: 700;
font-size: 16px;
cursor: pointer;
}
.weather-card {
background: rgba(255,255,255,0.05);
border: 1px solid #252525;
border-radius: 16px;
padding: 32px;
margin-top: 24px;
}
.temp {
font-size: 64px;
font-weight: 800;
margin: 12px 0;
}
.condition {
font-size: 18px;
color: #8a8a8a;
margin-bottom: 16px;
}
.details {
display: flex;
gap: 24px;
justify-content: center;
color: #8a8a8a;
}
Step 3: The JavaScript
Here's the core logic using the Open-Meteo API (free, no key needed):
const cityInput = document.getElementById('cityInput');
const searchBtn = document.getElementById('searchBtn');
const weatherCard = document.getElementById('weatherCard');
searchBtn.addEventListener('click', () => {
const city = cityInput.value.trim();
if (city) getWeather(city);
});
cityInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
const city = cityInput.value.trim();
if (city) getWeather(city);
}
});
async function getWeather(city) {
try {
// Step 1: Get coordinates from city name
const geoUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(city)}&count=1`;
const geoRes = await fetch(geoUrl);
const geoData = await geoRes.json();
if (!geoData.results || geoData.results.length === 0) {
alert('City not found!');
return;
}
const { latitude, longitude, name, country } = geoData.results[0];
// Step 2: Get weather data
const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t=temperature_2m,relative_humidity_2m,wind_speed_10m,weather_code`;
const weatherRes = await fetch(weatherUrl);
const weatherData = await weatherRes.json();
// Step 3: Display results
const current = weatherData.current;
document.getElementById('cityName').textContent = `${name}, ${country}`;
document.getElementById('temperature').textContent = `${Math.round(current.temperature_2m)}°C`;
document.getElementById('condition').textContent = getConditionText(current.weather_code);
document.getElementById('humidity').textContent = `${current.relative_humidity_2m}%`;
document.getElementById('wind').textContent = `${current.wind_speed_10m} km/h`;
weatherCard.style.display = 'block';
} catch (error) {
console.error('Weather fetch error:', error);
alert('Something went wrong. Try again.');
}
}
function getConditionText(code) {
const conditions = {
0: 'Clear sky', 1: 'Mainly clear', 2: 'Partly cloudy', 3: 'Overcast',
45: 'Foggy', 48: 'Rime fog', 51: 'Light drizzle', 53: 'Moderate drizzle',
61: 'Slight rain', 63: 'Moderate rain', 65: 'Heavy rain',
71: 'Slight snow', 73: 'Moderate snow', 75: 'Heavy snow',
80: 'Slight rain showers', 81: 'Moderate rain showers', 82: 'Violent rain showers',
95: 'Thunderstorm', 96: 'Thunderstorm with hail'
};
return conditions[code] || 'Unknown';
}
Try It Yourself
Open Deoit Editor, create these three files, and click Run. You now have a working weather app!
Pro tip: Use Deoit's built-in console to debug — any errors will show up instantly with line numbers.
Next Steps
Want to extend this project? Try adding:
- 5-day forecast — the Open-Meteo API supports daily forecasts
- Unit toggle — switch between Celsius and Fahrenheit
- Geolocation — auto-detect user's location with
navigator.geolocation - Search history — save recent searches to localStorage
All of these are great exercises for practicing JavaScript. Start building on Deoit — free, no sign-up required.