A modern, real-time investment portfolio tracking dashboard built with Next.js. Monitor your stock holdings, bonds, and cash allocations with live price updates and interactive visualizations.
- Real-Time Price Updates: Fetch current stock prices from Finnhub API
- Portfolio Allocation Chart: Visual pie chart showing asset distribution by category
- Holdings Table: Detailed breakdown of all holdings with current prices and values
- Category Breakdown: View portfolio value by category (Stocks, Bonds, Cash)
- Total Portfolio Value: Displays current total portfolio worth
- Config-Based: Simple JSON configuration file to manage your portfolio
- Responsive Design: Works seamlessly on desktop and mobile devices
- Type-Safe: Built with TypeScript for better developer experience
- Node.js 18+ and npm
- Finnhub API key (free tier available at finnhub.io)
-
Clone the repository
git clone https://github.com/tiant167/portfolio-dashboard.git cd portfolio-dashboard -
Install dependencies
npm install
-
Set up environment variables (local development)
Create a .env.local file in the root directory with:
FINNHUB_API_KEY=your_api_key_here
EDGE_CONFIG_URL=https://edge-config.vercel.com
EDGE_CONFIG_TOKEN=your_edge_config_read_tokenFor local testing without Vercel Edge Config, you can skip the Edge Config vars for now (the app will error with a clear message).
-
Configure your portfolio in Vercel Edge Config
- Go to your Vercel project β Edge Configs
- Create a new Edge Config (or use existing) and add a key named
portfolio - Set the value to your portfolio JSON (see βοΈ Configuration section below for format)
- Generate a read-only token and add
EDGE_CONFIG_URLandEDGE_CONFIG_TOKENto your.env.local
-
Run the development server
npm run dev
Open http://localhost:3000 in your browser. You should see the dashboard with your portfolio data.
portfolio-dashboard/
βββ app/
β βββ components/
β β βββ HoldingsTable.tsx # Detailed holdings table
β β βββ PortfolioPieChart.tsx # Asset allocation chart
β β βββ TotalValue.tsx # Total portfolio value display
β β βββ TrendGraph.tsx # Historical trend visualization
β βββ globals.css
β βββ layout.tsx
β βββ page.tsx # Main dashboard page
βββ pages/
β βββ api/
β βββ portfolio.ts # API endpoint for portfolio data
βββ portfolio.json # Portfolio configuration
βββ package.json
βββ tsconfig.json
βββ tailwind.config.ts
βββ next.config.ts
Portfolio data is stored in Vercel Edge Config using the portfolio key. This project reads that key via the @vercel/edge-config SDK helper in lib/edge-config.ts.
To configure your portfolio value in Edge Config, set the portfolio key to a JSON value with this shape:
{
"holdings": [
{
"symbol": "GOOGL", // Stock ticker symbol
"shares": 5, // Number of shares owned
"category": "Stocks", // Asset category
"targetPercentage": 30 // (optional) Target allocation % for this holding
}
],
"cash": 8000, // Cash balance
"categories": {
"Stocks": "#FF6384", // Category name and chart color
"Bonds": "#36A2EB",
"Cash": "#FFCE56"
}
}Optional Fields:
targetPercentage(number, 0β100): Desired allocation percentage for a holding. When provided for at least one holding, a "Target %" column appears in the dashboard holdings table showing your allocation targets vs. actual percentages. If omitted for all holdings, this column is hidden.
Notes:
- The app no longer relies on a local
portfolio.jsonfile in production β Edge Config is the source of truth. - See the Vercel Edge Config section below for setup steps, required env vars (
EDGE_CONFIG_URL,EDGE_CONFIG_TOKEN), and the SDK helper path (lib/edge-config.ts).
If Edge Config is not configured, the /api/portfolio endpoint will return a 500 error describing the missing key so you can fix the deployment configuration.
Returns current portfolio data with real-time prices.
Response:
{
"totalCurrentValue": 50000,
"categorizedValues": {
"Stocks": 35000,
"Bonds": 10000,
"Cash": 5000
},
"holdings": [
{
"symbol": "GOOGL",
"shares": 5,
"category": "Stocks",
"currentPrice": 170.50,
"value": 852.50
}
],
"categoriesConfig": { /* category colors */ }
}npm run dev- Start development servernpm run build- Build for productionnpm start- Start production servernpm run lint- Run ESLint
- Next.js 16 - React framework
- React 19 - UI library
- TypeScript - Type safety
- Recharts - Data visualization
- Tailwind CSS - Styling
- Finnhub API - Real-time stock prices
Never commit your .env.local file. Add it to .gitignore:
.env.local
For production deployments on Vercel, set environment variables in your project settings. Use the key name FINNHUB_API_KEY (do not commit it to source control).
Stock prices are fetched from the Finnhub API. Key points:
- Real-time and delayed quotes are available via the
/quoteendpoint - Free tier available for personal use; rate limits apply (see Finnhub pricing and docs)
For production use with higher volume, consider a paid plan or add server-side caching (Redis / Upstash) to avoid hitting rate limits.
The easiest way to deploy is using Vercel:
- Push your code to GitHub
- Connect your repository to Vercel
- Set
FINNHUB_API_KEYin Vercel's environment variables - Deploy
vercelThis is a standard Next.js app and can be deployed to any platform supporting Node.js (AWS, Heroku, Railway, etc.).
- Edit
portfolio.json - Add a new entry to the
holdingsarray:{ "symbol": "MSFT", "shares": 10, "category": "Stocks" } - Save and refresh your browser - prices update automatically
Edit the cash field in portfolio.json:
"cash": 15000- Add to
holdings: use the category name - Define color in
categories:"categories": { "Stocks": "#FF6384", "Bonds": "#36A2EB", "Cash": "#FFCE56", "Crypto": "#4BC0C0" }
This project now reads the portfolio JSON exclusively from Vercel Edge Config using the official SDK (@vercel/edge-config). The app no longer falls back to a local portfolio.json β deployments and environments must provide a portfolio key in Edge Config.
Why SDK: the SDK provides a simple, typed client and better integration with Vercel's runtime. The repository includes a small helper at lib/edge-config.ts that normalizes the SDK return value and exposes getPortfolioFromEdge().
Quick setup:
- In the Vercel dashboard go to Edge Configs β Create and add a key named
portfoliowhose value is your JSON (stringified). Example value:
{
"holdings": [
{ "symbol": "GOOGL", "shares": 5, "category": "Stocks" }
],
"cash": 8000,
"categories": { "Stocks": "#FF6384", "Bonds": "#36A2EB", "Cash": "#FFCE56" }
}- Install and configure the SDK (already added to
package.json):@vercel/edge-config. - Create a read-only token in the Edge Config settings and add it to your Vercel project env as:
EDGE_CONFIG_URL=https://edge-config.vercel.com
EDGE_CONFIG_TOKEN=<your_read_only_token>
- Deploy β the API endpoint
/api/portfoliowill read theportfoliokey via the SDK helper. If the key is missing, the API responds with 500 and a clear message indicating the missingportfoliokey so the issue can be fixed in Vercel.
Helper & code notes:
- Helper path:
lib/edge-config.tsβ it exportsgetPortfolioFromEdge()which returns the parsed JSON ornullon failure. - The API uses this helper (see
pages/api/portfolio.ts). - Do not expose the
EDGE_CONFIG_TOKENclient-side; store it in Vercel environment variables only.
Notes and limitations:
- Edge Config is optimized for small-to-medium JSON values. If your holdings list grows large, consider using a small DB (e.g., Supabase, Fauna, Postgres) and cache reads with a CDN.
- Edge Config is not a secrets store β never put API keys or private credentials in Edge Config.
- The SDK and Edge Config are intended for read-heavy, low-latency scenarios with infrequent writes.
Ensure your .env.local file exists with the correct API key, or add FINNHUB_API_KEY to your Vercel project environment variables.
- Verify your API key is valid
- Check Finnhub rate limits (free tier has limited requests)
- Wait a moment and refresh the page
Clear your browser cache or restart the dev server with npm run dev.
MIT
Contributions are welcome! Feel free to:
- Report bugs
- Suggest features
- Submit pull requests
For questions or issues, please open an issue on the GitHub repository.
Built with β€οΈ using Next.js and React
