Weather Widget
Rendered Component
Loading component...
Packages
npm install @fortawesome/react-fontawesome
npm install @fortawesome/free-solid-svg-icons
npm install framer-motion
yarn add @fortawesome/react-fontawesome
yarn add @fortawesome/free-solid-svg-icons
yarn add framer-motion
Code
'use client';
import React, { useState, useEffect } from 'react';
import { motion } from 'framer-motion';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
faCloudSun,
faTemperatureHigh,
faWind,
faTint,
faMapMarkerAlt,
} from '@fortawesome/free-solid-svg-icons';
const cities = [
{ name: 'New York', country: 'US' },
{ name: 'London', country: 'GB' },
{ name: 'Tokyo', country: 'JP' },
{ name: 'Paris', country: 'FR' },
{ name: 'Sydney', country: 'AU' },
// Add more cities as needed
];
const DEFAULT_CITY = cities[0]; // New York is the first city in the array
const WeatherWidget = () => {
const [weatherData, setWeatherData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [selectedCity, setSelectedCity] = useState(DEFAULT_CITY);
const [usingGeolocation, setUsingGeolocation] = useState(false);
const baseUrl = 'https://api.openweathermap.org/data/2.5/weather';
const fetchWeather = async (params) => {
const API_KEY = process.env.NEXT_PUBLIC_OPENWEATHERMAP_API_KEY;
const baseUrl = 'https://api.openweathermap.org/data/2.5/weather';
const queryParams = new URLSearchParams({
...params,
appid: API_KEY,
units: 'imperial', // Changed to imperial for Fahrenheit
});
try {
const response = await fetch(`${baseUrl}?${queryParams}`);
if (!response.ok) {
throw new Error('Failed to fetch weather data');
}
const data = await response.json();
setWeatherData(data);
setLoading(false);
} catch (error) {
console.error('Error fetching weather data:', error);
setError(error.message);
setLoading(false);
}
};
useEffect(() => {
if (usingGeolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
fetchWeather({
lat: position.coords.latitude,
lon: position.coords.longitude,
});
},
() => {
setUsingGeolocation(false);
fetchWeather({ q: `${DEFAULT_CITY.name},${DEFAULT_CITY.country}` });
}
);
} else {
fetchWeather({ q: `${selectedCity.name},${selectedCity.country}` });
}
}, [selectedCity, usingGeolocation]);
const handleCityChange = (event) => {
const cityName = event.target.value;
if (cityName === 'current') {
setUsingGeolocation(true);
} else {
setUsingGeolocation(false);
const city = cities.find((c) => c.name === cityName);
setSelectedCity(city);
}
};
if (loading)
return <div className='text-zinc-200'>Loading weather data...</div>;
if (error) return <div className='text-red-500'>Error: {error}</div>;
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className='bg-zinc-900 p-6 rounded-xl shadow-lg text-zinc-200'
>
<div className='flex justify-between items-center mb-4'>
<h2 className='text-2xl font-bold flex items-center'>
<FontAwesomeIcon icon={faCloudSun} className='mr-2' />
Weather
</h2>
<select
onChange={handleCityChange}
className='bg-zinc-800 text-zinc-200 rounded-md p-2'
value={usingGeolocation ? 'current' : selectedCity.name}
>
<option value='current'>Use My Location</option>
{cities.map((city) => (
<option key={`${city.name}-${city.country}`} value={city.name}>
{city.name}, {city.country}
</option>
))}
</select>
</div>
<div className='grid grid-cols-2 gap-4'>
<div>
<p className='text-4xl font-bold'>
{Math.round(weatherData.main.temp)}°F
</p>
<p className='text-gray-400 capitalize'>
{weatherData.weather[0].description}
</p>
</div>
<div className='space-y-2'>
<p className='flex items-center'>
<FontAwesomeIcon icon={faTemperatureHigh} className='mr-2 w-5 h-5' />
Feels like: {Math.round(weatherData.main.feels_like)}°F
</p>
<p className='flex items-center'>
<FontAwesomeIcon icon={faWind} className='mr-2 w-5 h-5' />
Wind: {Math.round(weatherData.wind.speed)} mph
</p>
<p className='flex items-center'>
<FontAwesomeIcon icon={faTint} className='mr-2 w-5 h-5' />
Humidity: {weatherData.main.humidity}%
</p>
<p className='flex items-center'>
<FontAwesomeIcon icon={faMapMarkerAlt} className='mr-2 w-5 h-5' />
{weatherData.name}, {weatherData.sys.country}
</p>
</div>
</div>
</motion.div>
);
};
export default WeatherWidget;
Details
The Weather Widget is a compact component that presents current weather conditions and forecasts. It\'s designed to be easily integrated into dashboards or as a standalone feature on websites and apps.
Usage
Import the WeatherWidget component and provide it with the necessary weather data, either through props or by connecting it to a weather API.
Examples
- Homepage weather display
- Travel app destination weather
- Smart home dashboard weather widget