A modern Chrome extension for real-time NBA scores, detailed box scores, and conference standings — with light/dark theme support and a favorite-team shortcut.
Built as a Manifest V3 extension with React 18, TypeScript, TanStack Query, and Tailwind CSS. Data is sourced from public ESPN endpoints.
Popup size: 540 × 600 px.
- 🏀 Live game scores — Real-time scoreboard that auto-refreshes every 30 seconds while games are in progress.
- 📋 Box-score view — Click any live game to open a full box score: team statistics, player stats tables, and game leaders. Press the back button to return to the scoreboard.
- 📊 Conference standings — Eastern and Western conference tables with win-loss records, winning percentage, and games-behind (including half-games).
- ⭐ Favorite team — Pin your team; its game is surfaced at the top of the list. Standings use the correct, dynamic season year.
- 📅 Date navigation — A carousel to browse games on past and future dates.
- 🌍 Timezone-aware — Queries the target date and the previous day to capture games that cross the UTC boundary, then filters by your local timezone so the list always matches your "today."
- ⚡ Background refresh — Data is pre-fetched in the background so the popup is fresh the moment you open it.
- 🌙 Light/dark theme — Toggleable, with your preference persisted in
chrome.storage. - 🛡️ Resilient by design — Client-side rate limiting (60 req/min) with exponential backoff and retry on HTTP 429, plus Zod runtime validation of every API response.
There are two ways to install the extension.
- Go to the Releases page and download
nba-scores-v1.0.0.zipfrom the latest release. - Unzip it anywhere on your machine, e.g.:
unzip nba-scores-v1.0.0.zip -d nba-scores
- Open Chrome and go to
chrome://extensions/. - Enable Developer mode (top-right toggle).
- Click Load unpacked and select the unzipped folder (the one containing
manifest.json). - Pin the NBA Scores icon to your toolbar and click it to open the popup.
- Clone the repository:
git clone https://github.com/RakshithBhat03/nba-scores.git cd nba-scores - Install dependencies:
npm install
- Configure environment variables (see Environment):
cp .env.example .env
- Build the extension:
npm run build
- Load it in Chrome:
- Go to
chrome://extensions/ - Enable Developer mode
- Click Load unpacked
- Select the
dist/folder
- Go to
The extension works on any site and in new-tab contexts.
- Node.js 18+
- npm (or yarn / pnpm)
npm run dev # Start the Vite dev server with HMR
npm run build # Type-check + production build into dist/
npm run preview # Serve the built dist/ locally
npm run type-check # TypeScript validation (no emit)
npm run lint # ESLint (zero-warning policy)The extension reads its data sources from Vite environment variables. Copy the example file and edit if you need different endpoints:
cp .env.example .env# ESPN scoreboard / box-score / teams endpoints
VITE_API_BASE_URL=https://site.api.espn.com/apis/site/v2/sports/basketball/nba
# ESPN core endpoint used for standings
VITE_CORE_API_BASE_URL=https://sports.core.api.espn.com/v2/sports/basketball/leagues/nbaThe defaults point at public ESPN endpoints. Rate limiting is enforced client-side to be respectful of those APIs.
- React 18 + TypeScript (strict mode) — UI framework
- TanStack Query v5 — server state, caching, and automatic refetch
- Tailwind CSS + shadcn/ui — styling and component library with dark-theme support
- Vite — build tool and dev server
- Zod — runtime validation of API responses
- Chrome Extension Manifest V3 — extension framework (popup entry)
src/
├── components/
│ ├── common/ # Header, ErrorBoundary, LoadingSpinner
│ ├── scores/ # ScoresList, GameCard, GamePreview, DateCarousel
│ ├── standings/ # ConferenceStandings, StandingsList, TeamStandingRow
│ ├── settings/ # ThemeToggle, FavoriteTeamSelector
│ └── ui/ # shadcn/ui primitives (Button, Card, Badge, Tabs, etc.)
├── hooks/ # Data fetching & state (useScores, useStandings, useGamePreview, ...)
├── services/ # API service + chrome.storage integration
├── lib/ # utils, validation, rate limiter
├── schemas/ # Zod schemas for API responses
├── types/ # TypeScript interfaces (game, standings, settings)
├── utils/ # logger, standings utilities
└── data/ # Static NBA team data
- Hooks in
src/hooks/fetch data via TanStack Query with sensiblestaleTimes (live games: 30s refetch; box score: 30s; standings: 10 min). services/api.tscentralizes all HTTP calls and runs them through a rate limiter.lib/rateLimiter.tscaps throughput at 60 req/min with exponential backoff and retries on HTTP 429.- Zod schemas validate every external response before it reaches the UI.
services/storage.tspersists theme and favorite-team settings inchrome.storage(falling back tolocalStorageoutside the extension context).
All data comes from public ESPN endpoints:
| Purpose | Endpoint base |
|---|---|
| Scoreboard, box scores, teams | https://site.api.espn.com/apis/site/v2/sports/basketball/nba |
| Standings | https://sports.core.api.espn.com/v2/sports/basketball/leagues/nba |
These hosts are declared in manifest.json host_permissions and the Content Security Policy so the extension can call them cross-origin.
Releases are published on the GitHub Releases page. Each release ships a ready-to-load dist/ build packaged as nba-scores-v<version>.zip. See Install → Option A for how to load it.
See CHANGELOG.md for what changed in each version.
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature - Make your changes and test thoroughly
- Run lint and type checks:
npm run lint && npm run type-check - Commit using a clear, conventional message (e.g.
feat:,fix:,docs:) - Push to your branch:
git push origin feature/your-feature - Open a pull request against
main
- Rakshith Bhat — @RakshithBhat03
This project is licensed under the MIT License — see the LICENSE file for details.
ESPN disclaimer: This project is an independent, open-source project and is not affiliated with, endorsed by, or supported by ESPN in any way. It uses unofficial ESPN endpoints, which may change, break, or be removed at any time without notice. All ESPN content — including names, logos, trademarks, and data — is the property of ESPN and/or its licensors. This code is provided for educational and personal use only.
NBA disclaimer: This is an unofficial NBA extension. All NBA-related trademarks and logos are the property of the National Basketball Association. This extension is not affiliated with or endorsed by the NBA.
If you encounter any issues or have suggestions, please open an issue.