This is not a Next.js project. It's a Vite-based SPA (Single Page Application) served from Perplexity AI's CDN. The build identifier is web-resources-build: 2026.4.21.646.
| Layer | Technology |
|---|---|
| Bundler | Vite (via Rolldown runtime) |
| UI Framework | React (SPA with <div id="root">) |
| Animation | Framer Motion |
| Data Fetching | React Query |
| Rich Text Editor | Lexical |
| Charting | VisX (GridRows, TooltipWithBounds, useTweenedPath) |
| i18n | Custom i18n module |
| Routing | Custom SPA router (sub-router, routes) |
| Styling | CSS bundle + dark/light theme via data-color-scheme attribute |
| Fonts | Custom: PPLX Sans Beta v2 (variable), FK Grotesk Neue |
- Finance-specific: FinancePriceChange, FinanceMover, FinanceStockHistoryPeriodUtils, FinanceStockHistoryCurrentPrice, FinanceKeyTickersGroup, FinancePortfolio
- Charts: PredictionMarketChart, DotGrid, LiveIndicator
- Content: MarkdownRenderer, MarkdownStreamer, MarkdownResponse, NewsArticleCard, TextBlockSkeleton
- Citations: CitationDomainBubble, CitationFavicon, CitationPile, CitationDomain, InlineCitationCaption
- Layout: CanonicalCard, CanonicalHeader, CanonicalContainer, CanonicalQuery, CanonicalLink
- UI Primitives: Badge, TabBar, ToggleGroup, ToggleTip, SelectButton, DropDownModal, AnimateHeight, Calendar
- Sharing: ShareDropdown, SocialShareLinks
- Data: Watchlist queries, quote fetching, earnings, financials, company profile, newswire documents
The page eagerly prefetches for a given ticker symbol:
/rest/finance/quote/{symbol}(with history)/rest/finance/profile/{symbol}/rest/finance/documents/{symbol}/rest/finance/earnings/{symbol}/rest/finance/financials/{symbol}
Perplexity uses an internal design system called "Aether" (aether-core) with a platform component layer (platform-core, platform-components). Icons come from a custom pplx-icons package. Cookie-based visitor tracking and session management are handled inline.
Aether is Perplexity's custom React design system. It's not open-source or publicly documented — it's bundled as aether-core and consumed internally by their platform-components and platform-core layers.
| Layer | Module | Role |
|---|---|---|
| aether-core | Design primitives | Tokens, base components, theming |
| platform-core | App infrastructure | Auth, routing, API clients, analytics, state |
| platform-components | App-level UI | Error boundaries, providers, device detection |
- Text / Heading — Typography primitives with variant system
- Button — Core interactive element
- Link — Navigation-aware anchor
- Icon — Icon system wrapper
- Spinner — Loading indicator
- Tooltip — Compound component (Provider, Trigger, Portal, Content, Arrow)
- Popper — Generic positioned floating element (Root, Anchor, Content, Arrow)
- Portal — React portal abstraction
- VisuallyHidden — Accessibility helper
- DismissableLayer — Click-outside / Escape dismiss behavior
- Presence — Mount/unmount animation wrapper
Spacing scale: 2xs, xs, sm, md, ml, lg, xl
Typography variants: micro, tiny, small, base, section-title, page-title, display, entry-title, editorial
Font sizes: 2xs through 4xl | Weights: normal → extrabold | Line heights: tight → loose | Tracking: tighter → widest
Colors:
- Foreground:
foreground,quiet,quieter,super,inverse,white - Semantic:
positive,negative,caution,warning - Surface:
background,underlay,raised,subtle - Finance-specific:
finance-positive,finance-negative
Border radius: sm through 3xl, full
Z-index layers: base, raised, navigation, overlay, modal, toast, critical
- Compound components — Tooltip and Popper use the Provider/Trigger/Content pattern (similar to Radix UI)
- Floating UI integration — Offset, Shift, Flip, Size, Hide, Arrow middleware for positioning
- Tailwind-based — Utility classes are generated/mapped from tokens (e.g.,
bg-background,px-md,rounded-xl) - Responsive helpers — Breakpoint-aware variant selectors
- Theme system —
ColorSchemeProviderwithdata-color-schemeattribute toggling light/dark - Context providers —
AetherProviderwraps the app, composingColorSchemeProvider,ModalStoreProvider,TooltipProvider,LinkComponentProvider,LabelsProvider,SurfaceProvider
Built on top of Aether, this provides:
- Auth: NextAuth-style session/sign-in/sign-out with CSRF
- Routing: Custom SPA router with
useRouter,useLocation,useSearchParams - API client: Typed REST client with SSE streaming support
- State: Reactive store factory (
createReactiveStore) with proxy-based selectors - Performance: Datadog integration, page timing vitals, transition perf tracking
- Feature flags: Query-count gating, subscription tier checks, A/B experimentation
- Logging: Structured browser logger + Sentry error reporting
Aether is heavily inspired by Radix UI primitives but is a custom implementation. It pairs with Tailwind CSS for styling and uses Floating UI for positioning. The token system is tight and opinionated — small set of scales, not overly configurable. The whole stack (Aether → Platform Core → Platform Components) gives Perplexity a vertically integrated UI framework purpose-built for their product.
| Concern | What They Use | Open-Source Equivalent |
|---|---|---|
| Bundler | Vite + Rolldown | Vite (Rolldown is experimental; Rollup works fine) |
| UI Library | React 19 | React 19 |
| Styling | Tailwind CSS | Tailwind CSS |
| State (server) | React Query v5 | @tanstack/react-query |
| State (client) | Zustand + Immer | zustand, immer |
| Routing | Custom SPA router | react-router or @tanstack/router |
They use VisX (Airbnb's low-level D3+React library), not a high-level chart library like Recharts.
Components you'd need:
- LinePath / AreaClosed — price history curves
- GridRows — horizontal grid lines
- Axis (Bottom/Left) — time and price axes
- TooltipWithBounds — crosshair tooltip on hover
- scaleLinear / scaleTime — D3 scales for mapping data to pixels
- useTweenedPath — animated transitions between time periods
This gives full control over segment coloring (green/red based on gain/loss), after-hours shading, and custom tick formatting. Higher-level libraries won't give you this.
REST API endpoints you need to serve:
GET /quote/{symbol}— current price, change, volume, market capGET /quote/{symbol}?with_history=true&history_period=1d— OHLC time-seriesGET /profile/{symbol}— company info, sector, descriptionGET /financials/{symbol}— income statement, balance sheet, cash flowGET /earnings/{symbol}— EPS estimates vs actuals, earnings datesGET /documents/{symbol}— SEC filings, news articles
Time periods supported: 1D, 5D, 1M, 6M, YTD, 1Y, 5Y, Max
Data sources (for your backend): Yahoo Finance API, Polygon.io, Alpha Vantage, or Financial Modeling Prep.
They use SSE (Server-Sent Events) — not WebSockets.
Key patterns:
- SSE endpoint at
/rest/sse/perplexity_askfor AI-generated answers - Cursor-based resume via
/rest/sse/subscribe/entry/{id} - JSON Patch (RFC 6902) for incremental state updates instead of full replacements
- Rate limiting: 20 reconnects/min, 120/hour
- Max 50 concurrent streams
AbortControllerfor cancellation
For stock price ticking, you'd need a separate WebSocket or SSE feed from your data provider.
Finance-specific:
PriceChangeIndicator— arrow + colored percentage (green/red/neutral), supports animated number transitionsFinanceAssetLogo— ticker logo with text-initial fallbackStockHistoryChart— the main interactive chart with period selectorFinanceMover— market movers cards (top gainers/losers)FinanceKeyTickersGroup— index ticker strip (S&P 500, NASDAQ, DOW)WatchlistTable— user's saved tickersPortfolioView— holdings with P&LEarningsCalendar— upcoming/past earnings with EPS dataPeerComparison— side-by-side metricsStockScreener— filterable table of stocks
General (from Aether):
TabBar— period selector (1D/5D/1M/etc.)ToggleGroup— segmented controlDropDownModal— filter/sort menusBadge— status labelsAnimateHeight— collapsible sectionsShareDropdown+SocialShareLinks— share functionalityMarkdownRenderer/MarkdownStreamer— for AI-generated analysis text
They use Intl.NumberFormat heavily:
- Currency formatting with locale awareness
- Percentage with 2 decimal places (change values divided by 100)
- Compact notation for large numbers (1.2B, 340M)
- Monospace font toggle for tabular alignment
- Light/dark mode via
data-color-schemeattribute on<html> - Cookie-persisted preference with system default fallback
- Finance-specific tokens:
finance-positive(green) andfinance-negative(red) with/10and/20opacity variants for backgrounds
- Datadog RUM v6 for performance metrics, session replay, and error tracking
- Page timing vitals (TTFB, TTV) tracked per route
- Custom duration vitals for key interactions
The AI "ask about this stock" feature uses:
- Lexical as the rich text editor for the input box
- SSE streaming for token-by-token AI response rendering
- MarkdownStreamer to render markdown as it arrives
- Citation system — inline numbered references with favicon bubbles and domain labels
If you want to build something similar without overengineering:
React 19 + Vite + Tailwind
@tanstack/react-query (server state)
zustand (client state)
@visx/visx (charts)
react-router (routing)
Intl.NumberFormat (formatting)
EventSource API (SSE streaming)
The hardest parts to replicate aren't the UI — they're the real-time data pipeline and the AI-powered analysis. The frontend is well-built but uses standard, accessible tools.