Prompt and context
This frontend question tests long-list interaction state, network boundaries, and accessibility. The challenge is not wiring an observer to a sentinel; it is defining a complete contract for loading, cancellation, failure, retry, position restoration, and alternate navigation.
What the interviewer evaluates
- Whether you can trigger loading with Intersection Observer or an equivalent mechanism and tune prefetch distance.
- Whether cursor state, request deduplication, and ordering prevent duplicate pages, out-of-order pages, and endless retries.
- Whether keyboard, screen-reader, zoom, and slow-network users get visible, operable states.
- Whether you recognize when infinite scroll needs pagination, a skip-to-content path, or position restoration.
Clarifying questions to ask
Confirm whether the list is a timeline, search result, or grid; whether ordering is stable; whether the server can issue cursors; and the page size and first-content target. Ask about deep links, back-navigation restoration, changing content heights, and mobile data constraints.
30-second answer framework
I would use stable cursor pagination and a removable loading sentinel. Load only when no request is active, another cursor exists, and the sentinel enters a prefetch window; deduplicate by cursor and item id while preserving order. Loading, failure, retry, and end-of-list states are focusable and visible. I would also offer a clear pagination or “Load more” path, skip-to-content, and position restoration so scrolling is not the only navigation method.
Step-by-step deep dive
1. Define the data and position contract
Prefer a cursor that is not shifted by new records. Return the next cursor and stable item ids. Keep known cursors, seen ids, and request state on the client so page numbers do not duplicate or skip items after an insertion. On navigation away, record the route, cursor, and anchor offset; restore the visible item when data is ready.
2. Design triggering and concurrency control
Intersection Observer emits a “near the bottom” signal; it does not authorize unbounded fetching. Before loading, check hasNext, loading, retry budget, and network state. Allow one request per cursor. Tune the prefetch threshold using item height and latency, and keep an explicit button for fast scrolling so repeated observer callbacks cannot create a burst.
3. Handle responses, cancellation, and failure
Validate that a response belongs to the expected cursor. Drop or merge late responses into the known set instead of appending blindly. Leaving the page may cancel an active request without clearing rendered data. A failure state explains the problem, exposes a retry button, and shows the loaded count. Automatic retries use bounded exponential backoff to avoid a request storm during an outage.
4. Provide accessible alternate paths
The sentinel must not be the only control. Provide a focusable “Load more,” explicit loading and end-of-list announcements, semantic list markup, stable headings, and visible focus. Long lists can expose pagination, a skip-duplicates path, and a “Back to list top” link. Focus movement needs a rule; inserting nodes must not send keyboard users to the end.
5. Verify performance, usability, and recovery
Test loading several pages with only Tab and Enter and verify that a screen reader perceives new content and failures. Exercise slow network, offline mode, rapid scrolling, repeated activation, back/forward navigation, 200% zoom, and dynamic image heights. Measure request count, duplicate rate, first-page and per-page latency, cancellation, and restoration success. If virtualization is needed, verify that it preserves semantics and focus.
Strong sample answer
I would base the list on a stable cursor and item ids, with client state for the consumed cursor, seen ids, active request, and retry count. The sentinel emits one load signal inside a prefetch window; the response must match the current cursor, and duplicate or out-of-order items are removed. Failure exposes a focusable retry control while offline mode keeps loaded content. Alongside infinite scroll, I provide “Load more,” pagination, or a jump control so keyboard and screen-reader users do not depend on a wheel. Back navigation restores an item anchor. I validate slow and offline networks, rapid scrolling, zoom, back/forward, and a screen reader.
Common mistakes
- Appending page numbers without considering duplicates or omissions after inserts.
- Starting a request on every observer callback without cursor deduplication or an in-flight lock.
- Retrying forever, or rendering retry as text that cannot receive focus.
- Making scrolling the only navigation, with no keyboard loading, pagination, or return path.
- Moving focus to the end when new nodes arrive, losing the reader’s position.
- Testing only a fast desktop connection and ignoring offline mode, zoom, dynamic height, and restoration.
Follow-up questions and answers
Why not use page-number pagination?
When records are inserted or removed, page boundaries move. Stable ordering plus a cursor reduces duplicates and omissions. If a snapshot is acceptable, a snapshot token can define one browsing boundary.
How do you tune a sentinel that fires too early?
Tune root margin from average item height, scroll speed, network latency, and cancellation rate, and make it observable. Prefetch remains bounded by hasNext, the concurrency lock, and the retry budget.
How do you preserve position when new items arrive at the top?
Record an anchor item and its relative offset, insert the new nodes, and compensate the scroll offset. For screen-reader and keyboard users, expose a “New items” control so they choose when to jump.
When should the design become paginated?
Use pagination when deep links, exact page jumps, result comparison, very long lists, or clear reading sections matter. Progressive loading can remain, but a visible pagination or “Load more” path must exist.