Skip to content

Design prompt: a news feed

A feed client protects the reader's place: new posts wait behind a pill, images land in reserved boxes, and only cards near the screen exist.

Predict first

Before you read: predict

ExamHall's announcements feed on the candidate home page is a windowed list built with TanStack Virtual's default options: only the cards near the screen are in the DOM. New announcements reach the page over a server-sent stream. Candidates who have scrolled down complain that the announcement they are reading jumps away whenever the exam office publishes one.

Which change stops the jump and still shows every new announcement, newest first, at the top of the feed?

Pick one

The 30-second version

A feed has two ends. Older pages come by cursor; newer posts, fetched as newer than the newest held, wait behind an "N new" pill, since an insert above moves what she reads. Render only the cards near the screen, size each image's box from the API's width and height, and keep loaded pages with the scroll offset.

A feed client protects the reader's place: new posts wait behind a pill, images land in reserved boxes, and only cards near the screen exist.

How it works

Step 1 of 13

Ask for the numbers. Post types, media, pinned posts, reactions, how often posts arrive, session length. They decide what must stay bounded.

Analogy: A stamp collector's printed album and her dealer's new-issue envelope

She reads and mounts her way through a one-country album, newest issues at the front.

In the stamp album In the feed client
each space printed to the catalogue's size before the stamp exists the image box from the API's width and height (step 8)
the dealer's envelope of new issues, the count on its flap, waiting on the desk pending posts behind the pill (steps 4–5)
"carry on after catalogue number 1234", never "page 31" the cursor (step 2)
only the spread open on the desk; the rest stands on the shelf the windowed list (step 10)
a ribbon that marks her place only in the same album the scroll offset kept with the loaded posts (step 12)

In real terms: the client changes the list only when the reader asks, and sizes every box before its content arrives.

Breaks down when: a printed space never changes, while a box resizes when a crop of another shape lands (step 9); and an album page holds still, while a windowed list repositions its cards as she scrolls.

Contrasting case: a notice board with three pinned notices: nothing arrives while she reads, nothing to window, nothing to hold.

Examples, run for real

4.1 ExamHall EXH-2920 → MR !3220: results day left a gap

On results day the exam office published 25 announcements while a candidate kept the home page open. She tapped the pill; its newer request asked for 20, and the client put them on top. Five announcements never appeared. MR !3220 follows the newer request's cursor until a page comes back short.

EXH-2920 newer than A-140, limit 20, one request · put on top: A-165 … A-146 (20) · never shown: A-145 … A-141 (5)
MR !3220 newer than A-140, then its cursor until a short page · 2 requests (20, 5) · put on top: A-165 … A-141 (25) · never shown: 0

The newer request returns the newest 20, so the five oldest new ones fall between that page and the posts she held (step 3).

4.2 Proof domain, ticketing: the "What's on" feed's two ends

A box office feed loads events 20 at a time; while a fan reads, four events are announced.

box office GET /events?limit=20 · E-200 … E-181 (20) · nextCursor E-181
box office GET /events?cursor=E-181&limit=20 · E-180 … E-161 (20) · nextCursor E-161
box office GET /events?newer_than=E-200&limit=20 · E-204 … E-201 (4) · nextCursor none (a short page) · pill "4 new"

The bottom end follows each reply's cursor; the top end asks for events newer than the newest held, and a short page means nothing is missing (steps 2–4).

4.3 The board for "Design the frontend of a social news feed" (worked, not executed)

Typical question Steps that answer it Mechanism owner
"Design the frontend of a social news feed." all, opened as two deep dives: the two ends, and the pill the round's phases: fsd-radio-framework
"New posts arrive while the user reads. What do you do with them?" 2–6 this lesson; the live channel: fsd-realtime-transport
"How do you keep memory and DOM size bounded after 30 minutes of scrolling?" 10–12 this lesson; how rows are measured: fsd-pagination-virtualization (no pack)

Follow-up ladder (this lesson's order, one rung per answer): pagination, then new posts while reading, then a long session, then images, then reactions offline.

Try it

Answer first, then check. Nothing is saved.

Question 1 of 7

A box office's "What's on" feed shows each event as a card: its poster at the card's full width, the title, then a Buy tickets button. The feed API returns { id, title, poster: { url } }, and the card renders <img src={poster.url} alt="" style="width:100%">. Every poster is 1200 × 900 pixels. While posters load, fans see the Buy tickets buttons move.

Which change keeps every button where it was first drawn?

Pick one

Question 2 of 7

On the candidate home page, the announcements feed is a windowed list in its own scroll area, loaded 20 at a time by cursor as she scrolls. Tapping an announcement opens its own page, which replaces the feed, so the feed's components unmount; Back brings the feed back. A candidate scrolls until the 41st announcement is at the top of the feed, opens it, then taps Back.

Which change puts that same announcement back at the top of the feed?

Pick one

Question 3 of 7

A box office's "What's on" feed shows 20 events per page in a scroll area feedArea, with an empty sentinel element after the last card. Its loader:

appendCards adds one card per event at the end of the list. Page 1 is on screen, and the reply for page 2 is slow. While it is on its way, a fan scrolls to the bottom, up a screen, to the bottom again, up a screen, and to the bottom once more. Then every reply arrives.

How many requests reach the server, and how many times does each page-2 event appear?

Code for this question
const io = new IntersectionObserver(async ([entry]) => {  if (entry.isIntersecting) await loadPage(nextCursor);}, { root: feedArea });io.observe(sentinel);async function loadPage(cursor) {  const { events, next } = await fetch(`/events?cursor=${cursor}`).then((r) => r.json());  appendCards(events);  nextCursor = next;}
Pick one

Question 4 of 7

A small institution's announcements page renders all of its announcements as a plain list in its own scroll area, with no windowing and the browser's default CSS. Hana has not scrolled since the page opened, and is reading the first announcement, A-60. Omar has scrolled down until A-31 sits at the top of his screen. The exam office publishes A-61, and both pages put it at the top of the list.

Whose screen still has, at its top, the announcement its reader was on?

Pick one

Question 5 of 7

The interviewer says: "Design the frontend of ExamHall's announcements feed: posts with images, pinned items, and a 'new announcement' pill during exam season." In 20 minutes, write your board: the requirements you would confirm, the components, the client data, the API, two deep dives with the follow-up you expect on each, and one estimate.

Then write your API in the shape of feed-api.template.json as my-feed-api.json, and run the check.

Question 6 of 7

A ticket-resale app's listings feed is a windowed list built with TanStack Virtual, overscan: 2, in a 720 px-tall scroll area. Every listing card is 180 px tall and renders 14 DOM elements. Pages of 20 listings load as the buyer scrolls, the next one when 5 loaded cards remain below the screen. A buyer reads for 30 minutes, moving on one card every 5 s, and stops with a card's top edge exactly at the top of the scroll area.

How many card elements are in the DOM then?

Pick one

Question 7 of 7

Each announcement's image comes from the API as { url, width, height }, the size of the uploaded original, and the card renders <img src={url} width={width} height={height} alt="" style="width:100%;height:auto"> above its Mark as read button. One announcement's original is 1200 × 900. On phones, the image service answers that URL with a 600 × 600 square crop.

On a phone whose cards are 360 px wide, what does that card's Mark as read button do as the image arrives?

Pick one

Go deeper

Pro tip
  1. Name the cursor and the "new posts" pill in your first two minutes: they are this prompt's two deep dives. When: the requirements phase of a feed round (choosing dives is fsd-radio-framework's). Status: SELF (the curriculum row's tip); not run.
Source
  • Front End Interview Handbook, Front End System Design (the curriculum row's URL): https://www.frontendinterviewhandbook.com/front-end-system-design · fetched 2026-10-04, saved as sources/fih-overview.txt. "News feed (e.g. Facebook)" among the application examples, and "Interface definition (API): Define the interface (API) between components in the product, functionality of each API, their parameters and responses". A guide's interview advice: valid as of 2026-10-04.
  • web.dev, Cumulative Layout Shift (CLS) (the curriculum row's URL): https://web.dev/articles/cls · fetched 2026-10-04, saved as sources/webdev-cls.txt. "Unexpected layout shifts can disrupt the user experience in many ways, from causing them to lose their place while reading if the text moves suddenly" (steps 4, 8). Valid as of 2026-10-04.
  • web.dev, Optimize Cumulative Layout Shift: https://web.dev/articles/optimize-cls · fetched 2026-10-04, saved as sources/webdev-optimize-cls.txt. "Always include width and height size attributes on your images and video elements" (step 8); "Seamlessly load the content offscreen and overlay a notice to the user that it's available" (steps 4–5); "If the image dimensions are different, this still causes some layout shift after the image loads" (step 9). Valid as of 2026-10-04.
  • TanStack Virtual (the curriculum row's URL): https://tanstack.com/virtual/latest, with its Introduction and Virtualizer API pages https://tanstack.com/virtual/latest/docs/introduction and https://tanstack.com/virtual/latest/docs/api/virtualizer · fetched 2026-10-04, saved as sources/tanstack-virtual.txt, sources/tanstack-virtual-intro.txt and sources/tanstack-virtual-api.txt. "The number of items to render above and below the visible area" (overscan, step 10); "The default 'start' preserves TanStack Virtual's existing top/left anchored behavior" (step 6). The runs use @tanstack/virtual-core 3.17.11. Valid as of 2026-10-04.
  • CSS Scroll Anchoring Module Level 1 (Editor's Draft, 30 September 2026): https://drafts.csswg.org/css-scroll-anchoring/ · fetched 2026-10-04, saved as sources/css-scroll-anchoring.txt. Among the suppression triggers: "The scroll offset of the scrollable element being zero", and a change to height or transform "on any element in the path from the anchor node to the scrollable element" (step 6). Valid as of 2026-10-04.
  • MDN, overflow-anchor: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/overflow-anchor · fetched 2026-10-04, saved as sources/mdn-overflow-anchor.txt. The page marks the property "Baseline 2026" and "Newly available"; scroll anchoring "adjusts scroll position to minimize content shifts" (step 6). Valid as of 2026-10-04.
  • MDN, <img>: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/img · fetched 2026-10-04, saved as sources/mdn-img.txt. "Including height and width enables the aspect ratio of the image to be calculated by the browser prior to the image being loaded" (step 8). Valid as of 2026-10-04.
  • MDN, Intersection Observer API: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API · fetched 2026-10-04, saved as sources/mdn-intersection-observer.txt. The callback runs "whenever a threshold is crossed in one direction or the other" (step 11). Valid as of 2026-10-04.

Checked against the tools and docs as of 2026-10-04.