Skip to main content

Overview

This guide will walk you through setting up and running the Weather Finder project, a beautiful weather forecast application that demonstrates React hooks, TypeScript, and API integration.
The Weather Finder app uses the free Open-Meteo API, so no API key is required. You can start building immediately!

Setup

1

Navigate to the project

From the repository root, navigate to the weather-finder directory:
cd weather-finder
2

Install dependencies

Install all required dependencies using pnpm:
pnpm install
This will install React 19, TypeScript, Vite, and all development dependencies including testing libraries.
3

Start the development server

Launch the Vite development server:
pnpm dev
The app will be available at http://localhost:5173

Project Structure

The Weather Finder app is organized as follows:
weather-finder/
├── src/
│   ├── components/      # React components
│   ├── hooks/          # Custom React hooks
│   ├── services/       # API service layer
│   ├── types/          # TypeScript type definitions
│   ├── utils/          # Helper functions
│   ├── App.tsx         # Main app component
│   └── main.tsx        # Application entry point
├── package.json
└── vite.config.ts

Understanding the Code

Main App Component

The App.tsx file demonstrates React best practices:
src/App.tsx
import { useWeather } from './hooks/useWeather';
import { SearchBar } from './components/SearchBar';
import { CurrentWeather } from './components/CurrentWeather';
import { WeatherForecast } from './components/WeatherForecast';
import { LoadingSpinner } from './components/LoadingSpinner';
import { ErrorMessage } from './components/ErrorMessage';
import './App.css';

function App() {
  const { status, data, error, search, savedCity } = useWeather();

  return (
    <div className="app">
      <header className="app-header">
        <h1 className="app-title">
          <span aria-hidden="true">🌤️</span> Weather Finder
        </h1>
        <p className="app-subtitle">Consulta el clima de cualquier ciudad del mundo</p>
      </header>

      <main className="app-main">
        <SearchBar
          onSearch={search}
          isLoading={status === 'loading'}
          initialValue={savedCity}
        />

        <div className="app-content">
          {status === 'idle' && (
            <div className="idle-state">
              <span className="idle-emoji" aria-hidden="true">🌍</span>
              <p>Busca una ciudad para ver su pronóstico del tiempo</p>
            </div>
          )}

          {status === 'loading' && <LoadingSpinner />}
          {status === 'error' && error && <ErrorMessage message={error} />}
          {status === 'success' && data && (
            <>
              <CurrentWeather data={data} />
              <WeatherForecast daily={data.daily} />
            </>
          )}
        </div>
      </main>
    </div>
  );
}

export default App;

Custom Hook: useWeather

The app uses a custom hook to manage weather data and API calls:
src/hooks/useWeather.ts
import { useState, useCallback, useEffect } from 'react';
import { geocodeCity, fetchWeather } from '../services/api';
import type { WeatherData, Status } from '../types/weather';

const LAST_CITY_KEY = 'weather-finder:last-city';

export function useWeather() {
  const [savedCity] = useState<string>(() => 
    localStorage.getItem(LAST_CITY_KEY) ?? ''
  );

  const [state, setState] = useState<WeatherState>({
    status: 'idle',
    data: null,
    error: null,
  });

  const search = useCallback(async (city: string) => {
    const trimmed = city.trim();
    if (!trimmed) return;

    setState({ status: 'loading', data: null, error: null });

    try {
      const { name, latitude, longitude, country } = await geocodeCity(trimmed);
      const { current, daily } = await fetchWeather(latitude, longitude);

      localStorage.setItem(LAST_CITY_KEY, trimmed);

      setState({
        status: 'success',
        data: { cityName: name, country, current, daily },
        error: null,
      });
    } catch (err) {
      setState({
        status: 'error',
        data: null,
        error: err instanceof Error ? err.message : 'Ocurrió un error inesperado.',
      });
    }
  }, []);

  // Auto-search the last city on mount
  useEffect(() => {
    const lastCity = localStorage.getItem(LAST_CITY_KEY);
    if (lastCity) search(lastCity);
  }, [search]);

  return { ...state, search, savedCity };
}
This custom hook demonstrates several React patterns:
  • State management with useState
  • Async operations with useCallback
  • Side effects with useEffect
  • LocalStorage persistence
  • Proper error handling

Available Commands

Here are all the commands you can use:
pnpm dev
# Starts the Vite dev server with hot module replacement

Testing Your Changes

The project includes Vitest for testing. Run the test suite:
pnpm exec vitest run
For watch mode during development:
pnpm exec vitest
Tests are located in the src/test/ directory and use @testing-library/react for component testing.

Building for Production

When you’re ready to deploy:
1

Build the project

pnpm build
This runs TypeScript compilation followed by Vite’s production build.
2

Preview the build

Test the production build locally:
pnpm preview
3

Deploy

The dist/ folder contains your production-ready files. Deploy to any static hosting service:
# Example: Deploy to Vercel
pnpm add -g vercel
vercel login
vercel --prod
Make sure to test your production build with pnpm preview before deploying to catch any build-specific issues.

Key Features to Explore

1. City Search with Geocoding

Type any city name to get weather data. The app uses the Open-Meteo Geocoding API to convert city names to coordinates.

2. Current Weather Display

See real-time weather conditions including temperature, humidity, wind speed, and weather codes.

3. 7-Day Forecast

View a weekly forecast with daily temperature ranges and weather conditions.

4. LocalStorage Persistence

The app remembers your last searched city and automatically loads it on return visits.

5. Responsive Design

Fully responsive layout that works beautifully on mobile, tablet, and desktop.

Try the Other Projects

Once you’re comfortable with Weather Finder, explore the other projects:

Gifs App

Learn about API integration with Giphy, state management for search history, and grid layouts.
cd ../gifs-app
pnpm install && pnpm dev

Hooks App

Study React hooks in isolation with practical examples like a traffic light component.
cd ../hooks-app
pnpm install && pnpm dev

Next Steps

Weather Finder Project

Explore the Weather Finder project in detail

API Integration

Deep dive into how the Open-Meteo API integration works

Custom Hooks

Learn about custom hooks like useWeather and useGifs

Component Patterns

Explore reusable component patterns and best practices

Troubleshooting

Port Already in Use

If port 5173 is already in use, Vite will automatically try the next available port. You can also specify a custom port:
pnpm dev -- --port 3000

Dependencies Issues

If you encounter dependency resolution issues:
rm -rf node_modules pnpm-lock.yaml
pnpm install

TypeScript Errors

Make sure your editor is using the workspace TypeScript version:
pnpm exec tsc --version
For VS Code users, press Cmd/Ctrl + Shift + P and select “TypeScript: Select TypeScript Version” → “Use Workspace Version”

Get Help

If you run into issues:
  • Check the GitHub Issues
  • Review the project README files
  • Open a new issue with details about your problem

Build docs developers (and LLMs) love