Use when building, editing, or reviewing React components, hooks, or pages in this codebase. Covers TanStack Query v5 data-fetching patterns, Recharts vs lightweight-charts selection, Tailwind CSS conventions, shared component usage (KPICard, LoadingSpinner, TimeframeToggle, SearchBar), and TypeScript interface rules. Triggers: component, hook, page, chart, dashboard, UI, Tailwind, TanStack, axios, styling, routing, React.
frontend/src/components/frontend/src/pages/frontend/src/hooks/| Use Case | Library | Why |
|---|---|---|
| Dashboard time-series (yields, spread, PCE, sector performance) | Recharts (<AreaChart>, <LineChart>) | Declarative, React-native, good for multi-line overlays |
| Stock OHLCV / candlestick / area price chart | lightweight-charts (createChart via ) |
useRef| Purpose-built for financial price charts; handles large OHLCV arrays efficiently |
Never mix the two for the same data type. Dashboard = Recharts. Stock detail price history = lightweight-charts.
Every data hook must follow this exact structure — no useEffect + useState for server data:
import { useQuery } from '@tanstack/react-query';
import { stockApi } from '../services/api';
export function useStockSummary(ticker: string) {
return useQuery({
queryKey: ['stock', ticker, 'summary'], // always an array, most specific last
queryFn: () => stockApi.getSummary(ticker),
staleTime: 4 * 60 * 1000, // see staleTime table below
refetchInterval: 5 * 60 * 1000, // omit for slow-changing data
enabled: !!ticker, // guard against empty params
retry: 2,
});
}
| Data type | staleTime | refetchInterval |
|---|---|---|
| Market overview, sectors, stock price, technicals, options | 4 * 60 * 1000 | 5 * 60 * 1000 |
| Treasury yields | 55 * 60 * 1000 | 60 * 60 * 1000 |
| PCE (monthly) | 5 * 60 * 60 * 1000 | omit |
| Earnings, insiders, analysts | 4 * 60 * 60 * 1000 | omit |
| News | 30 * 60 * 1000 | 30 * 60 * 1000 |
Always guard loading and error at the TOP of the component body, before any rendering or hooks that depend on data:
export function MyComponent({ ticker }: { ticker: string }) {
const { data, isLoading, error } = useStockSummary(ticker);
if (isLoading) return <LoadingSpinner />;
if (error || !data) return <p className="text-red-400 text-sm">Failed to load data.</p>;
// safe to use data here
return <div>...</div>;
}
| Component | Use for |
|---|---|
<KPICard> | Single metric display (price, % change, VIX level, PCE value) |
<LoadingSpinner /> | Loading state in every component |
<TimeframeToggle> | Any period selector (1d / 1w / 1m / 3m / 6m / 1y) |
<SearchBar> | Debounced ticker/company search — links to /stock/:ticker |
frontend/src/types/index.ts. Never define inline interface or type inside a component file.frontend/src/services/api.ts — import from there, not re-declared.types/index.ts first, then use it in the hook and api service.bg-gray-900, bg-gray-800, bg-gray-950bg-gray-800 rounded-xl p-4text-green-400text-red-400text-gray-400 text-smtext-white font-semibold| New file type | Location |
|---|---|
| Dashboard component | frontend/src/components/dashboard/ |
| Stock detail component | frontend/src/components/stock/ |
| Shared/reusable component | frontend/src/components/shared/ |
| Data hook | frontend/src/hooks/useMarket.ts or useStock.ts |
| New page | frontend/src/pages/ + add route in App.tsx |
| New interface | frontend/src/types/index.ts |