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:

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:

All of these are great exercises for practicing JavaScript. Start building on Deoit — free, no sign-up required.