Paramclasses Academy - Blog

Tutorials Details

←
Post 25 of 29
→

Project 2 — Building a Weather Application with API Integration in react.js

Project 2 — Building a Weather Application with API Integration in react.js

October 30, 2026

1. Introduction

In this tutorial, we will build a complete Weather Application using React.js and a real weather API.

A Weather Application allows users to search for a city and view its current weather information, such as temperature, humidity, wind speed, and weather conditions.

We will connect our React application to a weather API and dynamically display the information returned by the server.

This project will help you understand how real-world React applications communicate with external APIs.

Technologies Used:

  • React.js

  • JavaScript ES6+

  • HTML5

  • CSS3

  • Vite

  • WeatherAPI

  • Fetch API

  • React Hooks

2. Learning Objectives

After completing this tutorial, you will learn:

  1. How to create a React project using Vite.

  2. What an API is and how API integration works.

  3. How to create and configure a weather API key.

  4. How to fetch weather data using fetch().

  5. How to use async/await.

  6. How to manage API data using useState.

  7. How to use useEffect for API requests.

  8. How to implement city search functionality.

  9. How to display weather information dynamically.

  10. How to handle loading and error states.

  11. How to use environment variables in Vite.

  12. How to design a responsive Weather Application.

3. Understanding the Weather API

What is an API?

API stands for Application Programming Interface.

An API allows two different software applications to communicate and exchange information.

For example:

  • Our React application requests weather information.

  • The weather API receives the request.

  • The API retrieves the weather information.

  • The API sends the response in JSON format.

  • React displays the received information on the screen.

What is WeatherAPI?

WeatherAPI is an online weather service that provides weather information through HTTP requests.

We will use its Current Weather API.

Official website:

https://www.weatherapi.com/

Create Your API Key

Follow these steps:

  1. Open the WeatherAPI website.

  2. Create an account.

  3. Log in to your account.

  4. Find your API key in the account dashboard.

  5. Copy the API key.

  6. Keep it ready for your React project.

The API key is required to authenticate requests.

Example API Endpoint

https://api.weatherapi.com/v1/current.json?key=YOUR_API_KEY&q=London&aqi=no

Explanation:

  • current.json: Requests current weather information.

  • key: Contains your API key.

  • q: Specifies the city name.

  • aqi=no: Disables air quality information.

The API returns weather information in JSON format.

4. Create React Project

Open your terminal and execute:

npm create vite@latest weather-app -- --template react

Move into the project directory:

cd weather-app

Install dependencies:

npm install

Start the development server:

npm run dev

Open the local URL displayed in your terminal.

5. Project Folder Structure

Organize your application like this:

weather-app/
│
├── src/
│   ├── components/
│   │   ├── SearchBar.jsx
│   │   └── WeatherCard.jsx
│   │
│   ├── App.jsx
│   ├── App.css
│   ├── index.css
│   └── main.jsx
│
├── .env
├── package.json
└── index.html

Component responsibilities:

  • SearchBar: Accepts the city name.

  • WeatherCard: Displays weather information.

  • App: Manages API requests, application state, and errors.

6. Build the Weather Application

Step 1: Configure Environment Variables

Create a .env file in the root project directory.

VITE_WEATHER_API_KEY=YOUR_API_KEY_HERE

Replace YOUR_API_KEY_HERE with your actual API key.

Important:

  • Vite exposes frontend environment variables prefixed with VITE_.

  • These variables are included in the client-side application.

  • They are not secret or secure storage.

  • Do not use this method for private production API keys.

  • For a production application, use a backend server to protect the API key.

After changing .env, restart the Vite development server.

Step 2: Create SearchBar Component

Create src/components/SearchBar.jsx.

import { useState } from "react";

function SearchBar({ onSearch, loading }) {
  const [city, setCity] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();

    const searchCity = city.trim();

    if (!searchCity) return;

    onSearch(searchCity);
  };

  return (
    <form className="search-form" onSubmit={handleSubmit}>

      <input
        type="text"
        placeholder="Enter city name..."
        value={city}
        onChange={(e) => setCity(e.target.value)}
      />

      <button type="submit" disabled={loading}>
        {loading ? "Searching..." : "Search"}
      </button>

    </form>
  );
}

export default SearchBar;

Explanation:

  • useState() stores the city name.

  • onChange updates the input value.

  • onSubmit handles form submission.

  • preventDefault() prevents page reload.

  • trim() removes unnecessary spaces.

  • onSearch() sends the city name to the parent component.

  • loading disables the button while the request is running.

Step 3: Create WeatherCard Component

Create src/components/WeatherCard.jsx.

function WeatherCard({ weather }) {
  if (!weather) return null;

  return (
    <div className="weather-card">

      <div className="weather-location">
        <h2>
          {weather.location.name}
        </h2>

        <p>
          {weather.location.country}
        </p>
      </div>

      <div className="weather-main">

        <img
          src={`https:${weather.current.condition.icon}`}
          alt={weather.current.condition.text}
        />

        <div>
          <h1>
            {Math.round(weather.current.temp_c)}°C
          </h1>

          <p>
            {weather.current.condition.text}
          </p>
        </div>

      </div>

      <div className="weather-details">

        <div className="weather-info">
          <span>Feels Like</span>
          <strong>
            {weather.current.feelslike_c}°C
          </strong>
        </div>

        <div className="weather-info">
          <span>Humidity</span>
          <strong>
            {weather.current.humidity}%
          </strong>
        </div>

        <div className="weather-info">
          <span>Wind Speed</span>
          <strong>
            {weather.current.wind_kph} km/h
          </strong>
        </div>

        <div className="weather-info">
          <span>UV Index</span>
          <strong>
            {weather.current.uv}
          </strong>
        </div>

      </div>

      <p className="weather-updated">
        Local Time: {weather.location.localtime}
      </p>

    </div>
  );
}

export default WeatherCard;

Explanation:

  • weather contains the complete API response.

  • weather.location provides city and country information.

  • weather.current provides current weather details.

  • condition.icon provides the weather icon URL.

  • Math.round() rounds the temperature.

  • Conditional rendering prevents an empty weather card from appearing.

Step 4: Create Main App Component

Open src/App.jsx.

import { useState } from "react";

import SearchBar from "./components/SearchBar";
import WeatherCard from "./components/WeatherCard";

import "./App.css";

function App() {

  const [weather, setWeather] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");

  const API_KEY = import.meta.env.VITE_WEATHER_API_KEY;

  // Fetch Weather Data
  const fetchWeather = async (city) => {

    if (!API_KEY) {
      setError("API key is missing. Check your .env file.");
      setWeather(null);
      return;
    }

    setLoading(true);
    setError("");

    try {

      const response = await fetch(
        `https://api.weatherapi.com/v1/current.json?key=${API_KEY}&q=${encodeURIComponent(city)}&aqi=no`
      );

      const data = await response.json();

      if (!response.ok) {
        throw new Error(
          data.error?.message || "Unable to fetch weather data."
        );
      }

      setWeather(data);

    } catch (err) {

      setWeather(null);
      setError(err.message);

    } finally {

      setLoading(false);

    }
  };

  return (
    <div className="app-container">

      <div className="weather-container">

        <header className="app-header">
          <h1>Weather App</h1>
          <p>Find current weather around the world.</p>
        </header>

        <SearchBar
          onSearch={fetchWeather}
          loading={loading}
        />

        {loading && (
          <p className="status-message">
            Fetching weather information...
          </p>
        )}

        {error && (
          <p className="error-message">
            {error}
          </p>
        )}

        {!loading && weather && (
          <WeatherCard weather={weather} />
        )}

        {!loading && !weather && !error && (
          <p className="welcome-message">
            Search for a city to see its current weather.
          </p>
        )}

      </div>

    </div>
  );
}

export default App;

Step 5: Add CSS Styling

Open src/App.css.

* {
  box-sizing: border-box;
}

.app-container {
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 30px 16px;
  background: linear-gradient(135deg, #0f172a, #312e81, #1d4ed8);
}

.weather-container {
  width: 100%;
  max-width: 600px;
  padding: 30px;
  background: rgba(255, 255, 255, 0.96);
  border-radius: 20px;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.25);
}

.app-header {
  text-align: center;
  margin-bottom: 25px;
}

.app-header h1 {
  color: #312e81;
  font-size: 34px;
  margin-bottom: 8px;
}

.app-header p {
  color: #64748b;
}

.search-form {
  display: flex;
  gap: 10px;
}

.search-form input {
  flex: 1;
  min-width: 0;
  padding: 14px;
  border: 1px solid #cbd5e1;
  border-radius: 9px;
  font-size: 15px;
}

.search-form button {
  padding: 14px 20px;
  background: #4f46e5;
  color: white;
  border: none;
  border-radius: 9px;
  font-weight: bold;
  cursor: pointer;
}

.search-form button:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.weather-card {
  margin-top: 25px;
  padding: 22px;
  background: linear-gradient(135deg, #eef2ff, #dbeafe);
  border-radius: 15px;
}

.weather-location {
  text-align: center;
}

.weather-location h2 {
  margin-bottom: 5px;
  color: #1e293b;
}

.weather-location p {
  color: #64748b;
}

.weather-main {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 15px;
  margin: 20px 0;
}

.weather-main img {
  width: 100px;
  height: 100px;
}

.weather-main h1 {
  font-size: 48px;
  color: #312e81;
  margin: 0;
}

.weather-main p {
  color: #475569;
  margin-top: 5px;
}

.weather-details {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
}

.weather-info {
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: 15px;
  background: white;
  border-radius: 10px;
}

.weather-info span {
  color: #64748b;
  font-size: 14px;
}

.weather-info strong {
  color: #1e293b;
  font-size: 18px;
}

.weather-updated {
  margin-top: 18px;
  text-align: center;
  color: #64748b;
  font-size: 13px;
}

.status-message,
.welcome-message {
  text-align: center;
  color: #475569;
  margin-top: 25px;
}

.error-message {
  text-align: center;
  padding: 12px;
  margin-top: 20px;
  color: #b91c1c;
  background: #fee2e2;
  border-radius: 8px;
}

@media (max-width: 480px) {
  .weather-container {
    padding: 20px;
  }

  .search-form {
    flex-direction: column;
  }

  .search-form button {
    width: 100%;
  }

  .weather-main h1 {
    font-size: 38px;
  }
}

Step 6: Configure index.css

Open src/index.css.

* {
  margin: 0;
  padding: 0;
}

body {
  font-family: Arial, sans-serif;
}

Step 7: Check main.jsx

Ensure src/main.jsx contains:

import React from "react";
import ReactDOM from "react-dom/client";

import App from "./App.jsx";
import "./index.css";

ReactDOM.createRoot(
  document.getElementById("root")
).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

7. Understanding the API Integration

Let's understand the most important part of our project.

What is fetch()?

fetch() is a JavaScript function used to make HTTP requests to servers and APIs.

Example:

const response = await fetch("API_URL");

It returns a Promise that resolves to a Response object.

What is async/await?

async and await make asynchronous JavaScript code easier to read and manage.

Example:

async function getData() {
  const response = await fetch("API_URL");

  const data = await response.json();

  console.log(data);
}

What is response.json()?

It reads the response body and converts JSON data into a JavaScript object.

Why do we use try/catch?

try/catch handles errors that may occur during an API request or JSON processing.

Why do we check response.ok?

A fetch request does not automatically reject its Promise for HTTP errors such as 404 or 401.

We check response.ok to detect unsuccessful HTTP responses and display an appropriate error.

Why do we use finally?

The finally block executes whether the request succeeds or fails.

We use it to reset the loading state:

finally {
  setLoading(false);
}

8. How the Application Works

The application follows this workflow:

  1. The user enters a city name.

  2. SearchBar sends the city name to App.

  3. App starts the loading state.

  4. The application sends an HTTP request to WeatherAPI.

  5. The API processes the request.

  6. The API returns weather information in JSON format.

  7. React stores the received data using useState.

  8. WeatherCard displays the information.

  9. If something goes wrong, the application displays an error message.

Example API Response Structure:

{
  "location": {
    "name": "London",
    "country": "United Kingdom"
  },
  "current": {
    "temp_c": 18,
    "humidity": 65,
    "wind_kph": 12,
    "condition": {
      "text": "Partly cloudy",
      "icon": "//cdn.weatherapi.com/weather/icon.png"
    }
  }
}

This is a simplified illustrative response. Actual API responses contain additional fields.

9. Common Mistakes and Best Practices

Common Mistakes

  1. Forgetting to add the API key.

  2. Using the wrong API endpoint.

  3. Forgetting to restart Vite after changing .env.

  4. Not checking response.ok.

  5. Forgetting to use await with asynchronous operations.

  6. Not handling API errors.

  7. Accessing nested properties before data is available.

  8. Forgetting to encode city names in API URLs.

  9. Exposing private API credentials in frontend code.

Best Practices

  • Keep API logic organized.

  • Use async/await for readable asynchronous code.

  • Always handle loading and error states.

  • Validate user input before making requests.

  • Use environment variables for development configuration.

  • Protect private API credentials with a backend.

  • Use conditional rendering for API-dependent components.

  • Show meaningful error messages.

  • Keep reusable UI components separate.

10. Practice Exercises

Try these exercises yourself:

  1. Add a search button that can search for multiple cities.

  2. Display the wind direction.

  3. Display the atmospheric pressure.

  4. Add a Celsius/Fahrenheit temperature switch.

  5. Display the weather condition using dynamic icons.

  6. Add a recent-search history.

  7. Add a button to refresh weather information.

  8. Improve the UI with a dark mode.

  9. Display the last updated time.

  10. Add a five-day weather forecast using the appropriate forecast API endpoint.

11. Key Takeaways and Conclusion

In this tutorial, we built a Weather Application using React.js and WeatherAPI.

You learned:

  • How to create a React project using Vite.

  • How APIs work.

  • How to configure an API key.

  • How to make HTTP requests using fetch().

  • How to use async/await.

  • How to manage API data using useState.

  • How to display dynamic information.

  • How to handle loading and error states.

  • How to use environment variables.

  • How to build a responsive application.

API integration is an important skill for professional React development. It allows applications to retrieve and display real-world information dynamically.

←
Post 25 of 29
→