Streaming prices through Next.js WebSockets works fine, and that is exactly the problem. The naive version functions, but every price tick triggers a full-page re-render. The demo in this article builds the working dashboard first, then shows why the working version was too expensive and how to fix it.
How Next.js WebSockets Power a Live Trading Dashboard
Next.js WebSockets let a financial dashboard update prices the instant they change: the page shell renders on the server, and a socket.io-client connection streams asset prices into the browser. That combination is the subject of a February 2025 livestream from the Full Cycle channel, recorded as a warm-up for the channel's Full Stack & Full Cycle immersion event, a week-long build of a stock trading (home broker) application.
The presenter builds a small Express API that keeps assets, orders, and price histories in memory, then wires a WebSocket server alongside it using socket.io. On the frontend, Next.js, the React framework maintained by Vercel, renders a dashboard listing assets, orders, and a candlestick-style price chart. The presenter notes that both Next.js and socket.io were created by the same developer, Guillermo Rauch, and points to the Next.js showcase featuring companies such as Stripe, OpenAI, Nike, Netflix Jobs, and Notion.
The stream itself is part of a series of warm-up lives (Docker, Next.js, Go, and this Next.js session) that precede the immersion. For readers who prefer structured programs over event recordings, related offerings in this space include the Crazystack Typescript course at crazystack.com.br and the Bootcamp do Dev Doido by Gustavo Dev Doido, which follow the same build-along project format. This article, produced for Skala Blog, reconstructs the technical content of the stream rather than the event promotion.
Server Components vs Client Components: Where Does the Socket Live?
WebSockets require a client component in Next.js, because the socket connection, event listeners, and hooks all depend on the browser. A server component is rendered entirely on the server: it ships no hooks, no onClick handlers, and no event-driven JavaScript. The moment a file declares 'use client', Next.js renders it on the server first and then hydrates it in the browser, where interactivity runs.
The presenter stresses a naming subtlety: 'use client' does not mean the component renders only in the browser. It means the server does its part, sends the markup, and the browser finishes the interactive work. Only pure server components skip the browser entirely.
A practical split looks like this:
| Component type | Rendered where | Can use hooks/events | JS shipped to browser | Best for |
|---|---|---|---|---|
| Server component | Server, fully | No | None | Static shells, lists, layouts |
| Client component ('use client') | Server, then hydrated | Yes | Yes | Charts, sockets, forms |
| Mixed page | Shell server-side, interactive children hydrated | Per component | Only for client parts | Most real dashboards |
The stream's advice: start every page as a server component and extract only the interactive parts into client components. The less JavaScript that reaches the browser, the lighter the page, which also helps SEO, since search engines receive fully rendered HTML instead of the empty shell typical of a single-page application.
Building the Home Broker Page: Routing, SWR, and the Chart
The demo starts with npx create-next-app, answering yes to TypeScript, ESLint, Tailwind CSS, and the App Router. The result installs just three runtime dependencies: react, react-dom, and next. Routing follows the folder structure, so a page.tsx inside app/home-broker becomes the /home-broker route. The presenter initially scaffolds with a cached Next.js 14 but manually upgrades the package to version 15, released in 2024, to work against the current major.
Tailwind utility classes (the presenter calls out the 'utility-first' CSS pattern by name) handle layout: a full-height flex container, a sidebar for assets, and a main area for the chart and orders list. The presenter reports using Node.js 23.6.1 locally during the recording.
For data fetching, the page uses SWR, Vercel's data-hook library, with a small fetcher function that calls the Express API at localhost:8080/assets. SWR organizes when requests run, including refetch on window focus; it does not perform the request itself.
The chart comes from Lightweight Charts, TradingView's free charting library. Three React primitives hold it together: a useRef keeps the chart instance stable across renders, a useState stores the selected asset, and a useEffect with an empty dependency array creates the chart once on the browser. A second useEffect, watching the selected asset, fetches /assets/:id/histories and plots the series. Because the chart needs browser APIs, the whole page becomes a client component, which the presenter flags as the seed of the performance problem covered next.
Streaming Prices With socket.io-client
The WebSocket side installs socket.io-client and connects with io('[link removed]). The Express server emits one event per asset, named asset-{id}-price, carrying the new price and a timestamp. The client listens inside a useEffect that depends on the selected asset, so the listener attaches only when an asset is chosen.
Each incoming message updates the chart through the Lightweight Charts update method, converting the ISO 8601 timestamp with new Date(time).getTime() before plotting. The presenter highlights one cleanup step that many developers skip: returning socket.off() from the useEffect so the listener detaches when the component unmounts, preventing duplicated subscriptions on repeated renders.
Later, the demo extends the listener with a forEach over all assets, opening one subscription per asset so every quote in the sidebar updates live rather than only the selected one.
The Re-Render Trap: Why the Working Version Was Too Expensive
The functional dashboard hides a cost. To push live prices into the sidebar, the presenter first stores assets in a useState and updates one entry on every tick. React then re-renders the entire page component on each price event. Using Chrome's Rendering tab (paint flashing and highlight updates) plus the React DevTools profiler, the stream shows the whole tree flashing on every message.
In the stream's own measurement on the presenter's machine, browser CPU use sat around 4% with the naive version and dropped to roughly 2 to 2.5% after the fix, even with price events firing for every asset. These are demo-workload figures from the recording, not a benchmark of any production system.
The fix in the demo removes the state entirely: since the prices only need to display, the component writes them straight into specific DOM nodes via document.querySelector and innerHTML, so a tick touches one element instead of the tree. When state genuinely is needed, the presenter recommends three structural alternatives, compared below:
| Approach | Re-render scope | Fit for streaming prices |
|---|---|---|
| useState at page root | Whole page tree | Poor; every tick re-renders everything |
| Context API near the layout | Everything under the provider | Poor; providers tend to sit at the root |
| External store (Zustand, Redux, MobX) | Only components selecting the changed slice | Good; subscriptions are per component |
Splitting the page into AssetList, AssetChart, and OrdersList components also helps: a state change inside one child no longer re-renders its siblings. For functions recreated on every render, useCallback memoizes the reference; useMemo does the same for computed values, both relying on pure functions returning the same result for the same inputs. The presenter adds that React 19, released in 2024, deprecated forwardRef, one of several API rough edges discussed in the React community's feedback threads.
Webhooks vs WebSockets: Which Notification Fits Which Job?
A viewer question in the stream draws a clean line between the two mechanisms. A webhook is a one-shot HTTP call: a payment provider such as Stripe or Pagar.me calls your URL once when a payment clears, sometimes days after the purchase. A WebSocket is a persistent connection for continuous, bidirectional data.
The rule of thumb from the demo: if an event fires occasionally and a single HTTP request carries all the information, a webhook is simpler and cheaper. If data flows continuously, as asset prices do on a trading screen, a WebSocket justifies the open connection. Both can coexist in the same system; the trading demo itself uses plain HTTP for the initial history fetch and a WebSocket only for the live stream.
Frequently Asked Questions
- Do WebSockets in Next.js require a client component? Yes. Sockets, event listeners, and hooks run only in the browser, so the code that opens the connection must live inside a file marked 'use client'. Server components can still render everything around it, keeping the shipped JavaScript small.
- Can a server component OpenAI WebSocket connection? No. A server component renders once on the server and has no browser APIs. The presenter's rule: anything needing onClick, WebSocket, geolocation, or speech APIs must be a client component.
- Which versions did the stream use? The recording ran on Node.js 23.6.1 and targeted Next.js 15, released in 2024, alongside React 19. The presenter had to upgrade manually after a cached create-next-app scaffolded Next.js 14.
- Is SWR mandatory for fetching assets in Next.js? No. SWR is a convenience hook from Vercel that manages caching and refetch timing; plain fetch or axios works too. The stream uses SWR for the initial asset and order loads and a WebSocket for live updates.
Turn Your Own Streamed Lessons Into an Article
The strongest insight from this stream is that working code and well-built code are different things: the dashboard ran fine until someone measured its re-renders. If you explain lessons like that on YouTube, whether in a tutorial, a live code session, or an interview, that knowledge is often stuck in a two-hour video few people will scrub through.
Skala Blog turns those recordings into structured, searchable articles: paste a YouTube URL at Skala Blog, the video is transcribed, and the transcript becomes a written piece your readers can scan and search.
Fork this article
Start a new branch from the same video, shaped your way. You keep the credit; the original keeps the attribution.
A fork in another language is filed as a translation of this article, so the two pages point at each other. You can unlink it later from the editor.
0/240
You are creating
- Format
- For
- Language
- Source
- Your angle
You will be asked to sign in before it is generated.
Buy credits