0:00
/
0:00
Transcript

Build a Live Stock Price Tracker with React, Tailwind CSS & Alpha Vantage API

Looking to build your own real-time stock market tracker app? In this hands-on tutorial, we’ll use React, Tailwind CSS, and the Alpha Vantage API to create a sleek and functional live stock price tracker.

Whether you’re a frontend dev or a beginner trying to get into finance tech, this is a project-based learning opportunity that teaches practical skills while building something useful.

In this project, you'll learn how to:

- Fetch live stock prices from an API using React hooks

- Manage dynamic lists and user input

- Display loading states and error handling

- Style your app with Tailwind CSS for a professional look

Perfect for beginners and intermediate developers looking to boost their React skills!

🛠️ What We'll Build

  • 🔍 A stock search bar with live suggestions

  • 💰 A dashboard showing real-time prices

  • 📉 Dynamic price color change (green/red)

  • 📱 Clean Tailwind-powered responsive UI

  • 🌐 API integration with Alpha Vantage (free key!)


🚀 Technologies Used

  • React + Vite

  • Tailwind CSS

  • Alpha Vantage API (Get your key)

  • React hooks: useState, useEffect, fetch

🧱 Project Setup

bash

CopyEdit

npm create vite@latest stock-tracker --template react cd stock-tracker npm install npm install -D tailwindcss postcss autoprefixer npx tailwindcss init -p

Tailwind Setup (tailwind.config.js):

js

CopyEdit

content: [ "./index.html", "./src/**/*.{js,ts,jsx,tsx}", ],

Tailwind in index.css:

css

CopyEdit

@tailwind base; @tailwind components; @tailwind utilities;


🔗 API Setup with Alpha Vantage

Sign up at AlphaVantage.co and grab your API key.

Example fetch call:

js

CopyEdit

const fetchStockPrice = async (symbol) => { const res = await fetch(`https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${symbol}&apikey=YOUR_KEY`); const data = await res.json(); return data['Global Quote']; };


📊 Displaying Stock Data

Example JSX:

jsx

CopyEdit

<div className="p-4 bg-white shadow-md rounded"> <h2 className="text-xl font-bold">{stockName}</h2> <p className={`text-lg ${priceChange > 0 ? 'text-green-500' : 'text-red-500'}`}> ${stockPrice} ({priceChange}%) </p> </div>


🎨 UI Tips with Tailwind CSS

  • Use grid-cols-2 or flex justify-between for layouts

  • Add a search input with a dropdown using absolute, bg-white, z-10

  • Animate loading state with animate-pulse


📱 Optional Enhancements

  • Save recent stock symbols to localStorage

  • Add a chart using Chart.js or Recharts

  • Show currency, volume, or market cap


Source Code:

import React, { useState, useEffect } from "react";

// StockPrice component fetches and displays the price for a given ticker
function StockPrice({ ticker = "" }) {
  const [price, setPrice] = useState(null); // Holds the fetched price
  const [loading, setLoading] = useState(true); // Loading state
  const [error, setError] = useState(null); // Error state

  useEffect(() => {
    // Fetch stock price from Alpha Vantage API
    const fetchStockPrice = async () => {
      try {
        setLoading(true);
        const apiKey = import.meta.env.VITE_ALPHAVANTAGE_API_KEY; // Uncomment and set your API key
        const response = await fetch(
          `https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=${ticker}&apikey=${apiKey}`
        );
        if (!response.ok)
          throw new Error(`HTTP error! status: ${response.status}`);
        const data = await response.json();
        // Check if the API returned a valid price
        if (data["Global Quote"] && data["Global Quote"]["05. price"]) {
          setPrice(parseFloat(data["Global Quote"]["05. price"]).toFixed(2));
          setError(null);
        } else {
          setError("Could not retrieve price for this ticker.");
          setPrice(null);
        }
      } catch (e) {
        setError(`Error fetching data: ${e.message}`);
        setPrice(null);
      } finally {
        setLoading(false);
      }
    };

    fetchStockPrice(); // Call the fetch function when ticker changes
  }, [ticker]);

  return (
    <div className="bg-gradient-to-br from-white via-gray-50 to-gray-200 shadow-lg rounded-xl p-6 m-2 w-full max-w-xs flex flex-col items-center border border-gray-200 hover:shadow-2xl transition-shadow duration-200">
      {/* Display the ticker symbol */}
      <h3 className="text-xl font-bold mb-2 text-gray-800 tracking-wide uppercase letter-spacing-wider">
        {ticker}
      </h3>
      {/* Show loading spinner while fetching */}
      {loading && (
        <div className="flex items-center gap-2">
          <span className="inline-block w-4 h-4 border-4 border-blue-400 border-t-transparent rounded-full animate-spin"></span>
          <p className="text-blue-500 font-medium">Loading price...</p>
        </div>
      )}
      {/* Show error message if there is an error */}
      {error && <p className="text-red-500 font-semibold">{error}</p>}
      {/* Show the price if loaded successfully */}
      {!loading && !error && price !== null && (
        <p className="text-3xl font-extrabold text-green-600 mt-2">${price}</p>
      )}
      {/* Show message if no data is available */}
      {!loading && !error && price === null && (
        <p className="text-gray-500">No data available.</p>
      )}
    </div>
  );
}

// Main StockPriceTracker component manages the list of tickers and input
export default function StockPriceTracker() {
  const [tickers, setTickers] = useState(["NVDA", "MSFT", "GOOGL"]); // Initial tickers
  const [input, setInput] = useState(""); // Input for new ticker

  // Add a new ticker to the list
  const addTicker = () => {
    const val = input.trim().toUpperCase();
    if (val && !tickers.includes(val)) {
      setTickers([...tickers, val]);
      setInput("");
    }
  };

  return (
    <div className="min-h-screen bg-gradient-to-br from-blue-50 via-white to-gray-100 flex flex-col items-center py-12 px-4">
      {/* App title */}
      <h1 className="text-6xl font-extrabold mb-8 text-gray-900 tracking-tight drop-shadow">
        My Stock Tracker
      </h1>
      {/* Input and Add button for new tickers */}
      <div className="flex mb-8 w-full max-w-md">
        <input
          className="border border-gray-300 rounded-l-lg px-4 py-3 focus:outline-none focus:ring-2 focus:ring-blue-400 w-full text-lg transition"
          type="text"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Add ticker (e.g. AAPL)"
        />
        <button
          className="bg-blue-600 text-white px-6 py-3 rounded-r-lg font-semibold hover:bg-blue-700 transition-colors duration-150 shadow"
          onClick={addTicker}
        >
          Add
        </button>
      </div>
      {/* Display StockPrice components for each ticker */}
      <div className="flex flex-wrap justify-center gap-6 w-full max-w-4xl">
        {tickers.map((ticker) => (
          <StockPrice key={ticker} ticker={ticker} />
        ))}
      </div>
    </div>
  );
}

Discussion about this video