Claude Code से React Virtual Scrolling लागू करें
Claude Code और TanStack Virtual से virtual scrolling, variable height, accessibility और Playwright checks सीखें।
Virtual scrolling कब जरूरी है
Virtual scrolling का मतलब है कि बहुत लंबी list में सिर्फ वही rows render हों जो screen पर दिख रही हैं, साथ में थोड़ा सा buffer ऊपर और नीचे। अगर आपके पास 10000 logs, customers, chat messages या search results हैं, तो सभी rows को DOM में mount करना browser को बेवजह भारी कर देता है। DOM browser की element tree है जिससे layout, paint, events और accessibility information बनती है। User को केवल 20 rows दिखती हैं, लेकिन browser हजारों hidden nodes संभाल रहा होता है।
Claude Code React component जल्दी बना सकता है, पर अगर prompt सिर्फ “virtual scroll बना दो” है, तो output अक्सर demo जैसा होगा। Production में row height, keyboard navigation, screen reader context, image load के बाद height change, detail page से लौटने पर scroll restore, mobile width और tests सभी जरूरी हैं। इस article में हम Claude Code को बेहतर brief देना, TanStack Virtual से fixed और variable height list बनाना, accessibility जोड़ना और Playwright से जांच करना देखेंगे।
Use cases साफ हैं: log viewer, customer list, chat history, search results और admin table। Log viewer में deploy या error logs हजारों lines तक जाते हैं। Customer list में CRM filters, selection और detail screen होते हैं। Chat history में long text, avatar और attachments आते हैं। Search results filter बदलते ही redraw होते हैं। Admin table में columns, permissions और actions जुड़े होते हैं। Server से next page load करना हो तो infinite scroll भी पढ़ें। Overall rendering सुधार के लिए performance optimization useful है।
Claude Code को बेहतर brief दें
Virtual scrolling में “चल रहा है” काफी नहीं है। Prompt में implementation के साथ review criteria भी लिखें।
React 18 + TypeScript में virtualized log viewer implement करें।
Requirements:
- @tanstack/react-virtual use करें।
- 10000 से ज्यादा rows support हों, पर सभी DOM में mount न हों।
- Default row height 44px हो।
- role, aria-label, aria-posinset, aria-setsize जोड़ें।
- 390px mobile width पर horizontal page overflow न हो।
- overscan value क्यों चुनी, explain करें।
- Playwright test से scroll और mobile width verify करें।
- Final code को TanStack Virtual official docs से compare करें।
इस prompt से Claude Code को पता चलता है कि goal सिर्फ fast list नहीं, बल्कि reviewable UI है। Customer list में fields बदलेंगे, chat में author और body होंगे, search result में title और snippet होगा, लेकिन acceptance criteria वही रहेंगे।
Fixed height log viewer
React के लिए@tanstack/react-virtual practical choice है। यह headless utility है: visible items, offsets और total height calculate करती है, पर HTML और CSS आप control करते हैं। Official reference के लिए TanStack Virtual docs और Virtualizer API देखें।
npm install @tanstack/react-virtual
नीचे fixed height log viewer है। Outer div scroll करता है, inner div total height रखता है, और visible rowstranslateYसे अपनी जगह रखी जाती हैं।
import { useRef } from "react";
import { useVirtualizer } from "@tanstack/react-virtual";
type LogRow = {
id: string;
level: "info" | "warn" | "error";
message: string;
createdAt: string;
};
export function VirtualLogViewer({ rows }: { rows: LogRow[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const rowVirtualizer = useVirtualizer({
count: rows.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 44,
overscan: 12,
getItemKey: (index) => rows[index]?.id ?? index,
});
return (
<section aria-labelledby="log-heading">
<h2 id="log-heading">Application logs</h2>
<div
ref={parentRef}
data-testid="virtual-log-viewport"
role="list"
aria-label={`Application logs, ${rows.length} rows`}
style={{
height: 520,
overflow: "auto",
border: "1px solid #d4d4d8",
borderRadius: 6,
}}
>
<div
style={{
height: rowVirtualizer.getTotalSize(),
position: "relative",
width: "100%",
}}
>
{rowVirtualizer.getVirtualItems().map((virtualRow) => {
const row = rows[virtualRow.index];
if (!row) return null;
return (
<div
key={virtualRow.key}
role="listitem"
aria-posinset={virtualRow.index + 1}
aria-setsize={rows.length}
style={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: `${virtualRow.size}px`,
transform: `translateY(${virtualRow.start}px)`,
display: "grid",
gridTemplateColumns: "92px 72px minmax(0, 1fr)",
gap: 12,
alignItems: "center",
padding: "0 12px",
boxSizing: "border-box",
borderBottom: "1px solid #eee",
}}
>
<time dateTime={row.createdAt}>{row.createdAt}</time>
<strong>{row.level.toUpperCase()}</strong>
<span style={{ overflowWrap: "anywhere" }}>{row.message}</span>
</div>
);
})}
</div>
</div>
</section>
);
}
overscan viewport के बाहर extra rows render करता है। कम overscan से fast scroll में blank gap दिख सकता है। ज्यादा overscan से DOM फिर बड़ा हो जाता है। Text logs में 8 से 16 test करें। Heavy rows में, जैसे avatar, chart या code highlight, कम value से शुरू करके measure करें।
Variable height chat history
Chat, support comments और AI response logs में row height fixed नहीं होती। Message length, images, attachments और error banners height बदलते हैं। ऐसे में estimate दें और rendered element को measure करें।
import { useRef } from "react";
import { useVirtualizer } from "@tanstack/react-virtual";
type Message = {
id: string;
author: string;
body: string;
avatarUrl?: string;
};
export function VirtualChatHistory({ messages }: { messages: Message[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: messages.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 96,
overscan: 8,
getItemKey: (index) => messages[index]?.id ?? index,
});
return (
<div
ref={parentRef}
role="log"
aria-label="Chat history"
style={{ height: 520, overflow: "auto" }}
>
<div style={{ height: virtualizer.getTotalSize(), position: "relative" }}>
{virtualizer.getVirtualItems().map((virtualItem) => {
const message = messages[virtualItem.index];
if (!message) return null;
return (
<article
key={virtualItem.key}
data-index={virtualItem.index}
ref={virtualizer.measureElement}
style={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
transform: `translateY(${virtualItem.start}px)`,
padding: "12px 16px",
boxSizing: "border-box",
}}
>
{message.avatarUrl ? (
<img
src={message.avatarUrl}
alt=""
width={32}
height={32}
loading="lazy"
onLoad={() => virtualizer.measure()}
/>
) : null}
<p style={{ margin: 0, fontWeight: 700 }}>{message.author}</p>
<p style={{ margin: "4px 0 0", overflowWrap: "anywhere" }}>
{message.body}
</p>
</article>
);
})}
</div>
</div>
);
}
Images देर से load होती हैं, इसलिए height drift हो सकता है। Width और height reserve करें, image load पर remeasure करें। Chat product में यह भी तय करें कि new message आने पर view bottom पर pinned रहे या user की current reading position बची रहे।
Accessibility और keyboard
Virtual list में सभी rows DOM में नहीं होतीं, इसलिए list purpose, total count और current position बताना जरूरी है। Customer list में arrow keys, PageUp, PageDown, Home और End काम करने चाहिए।
import type { KeyboardEvent } from "react";
type KeyboardParams = {
activeIndex: number;
rowCount: number;
setActiveIndex: (index: number) => void;
scrollToIndex: (index: number) => void;
};
export function handleVirtualListKeyDown(
event: KeyboardEvent,
{ activeIndex, rowCount, setActiveIndex, scrollToIndex }: KeyboardParams,
) {
const lastIndex = Math.max(0, rowCount - 1);
let nextIndex = activeIndex;
if (event.key === "ArrowDown") nextIndex = Math.min(lastIndex, activeIndex + 1);
if (event.key === "ArrowUp") nextIndex = Math.max(0, activeIndex - 1);
if (event.key === "PageDown") nextIndex = Math.min(lastIndex, activeIndex + 10);
if (event.key === "PageUp") nextIndex = Math.max(0, activeIndex - 10);
if (event.key === "Home") nextIndex = 0;
if (event.key === "End") nextIndex = lastIndex;
if (nextIndex !== activeIndex) {
event.preventDefault();
setActiveIndex(nextIndex);
scrollToIndex(nextIndex);
}
}
Focus directly row पर रखने से problem हो सकती है, क्योंकि scroll के बाद वही row unmount हो सकती है। Stable pattern यह है कि focus container पर रहे और active rowaria-activedescendantसे बताई जाए। Detail page से लौटते समयscrollToprestore करें, पर key में filter और sort भी रखें। Related guidance के लिए accessibility guide देखें।
Playwright check और review prompt
Virtual scroll को केवल आंखों से approve न करें। Mobile width, target row, horizontal overflow और console errors check करें।
import { expect, test } from "@playwright/test";
test("virtual log viewer scrolls without horizontal overflow", async ({ page }) => {
const errors: string[] = [];
page.on("console", (message) => {
if (message.type() === "error") errors.push(message.text());
});
await page.setViewportSize({ width: 390, height: 844 });
await page.goto("/debug/virtual-log-viewer");
const viewport = page.getByTestId("virtual-log-viewport");
await expect(viewport).toBeVisible();
const before = await viewport.boundingBox();
await viewport.evaluate((node) => {
node.scrollTop = 2400;
});
await expect(page.getByText("Log #250")).toBeVisible();
const after = await viewport.boundingBox();
expect(after?.width).toBe(before?.width);
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(
await page.evaluate(() => document.documentElement.clientWidth),
);
expect(errors).toEqual([]);
});
Claude Code से final review इस तरह मांगें:
इस React virtual scrolling implementation को review करें।
Check करें:
- क्या यह TanStack Virtual official API follow करता है?
- fixed height और variable height logic अलग हैं?
- overscan कम तो नहीं है जिससे blank gap आए?
- role, aria और keyboard behavior coherent हैं?
- image load के बाद height remeasure होती है?
- detail page से लौटने पर scroll position restore होती है?
- SSR या hydration initial height jump तो नहीं बनाते?
- Playwright mobile width और scrolled row verify करता है?
Pitfalls, CTA और actual result
Common pitfalls हैं: variable height को fixed मान लेना, overscan बहुत कम रखना, overscan बहुत ज्यादा रखना, keyboard भूलना, screen reader को total count न देना, scroll restore न करना, image load के बाद height न मापना, SSR mismatch, और long text से mobile width टूटना। Mental model याद रखें: scrollTop -> visible range -> overscan -> virtual rows -> translateY -> real measurement.
अगर आपकी team इस pattern को real log viewer, customer table, search page या chat product में लगाना चाहती है, तो Claude Code training and consultation में requirements, prompts, CLAUDE.md, accessibility review और Playwright proof को repository के साथ set किया जा सकता है। Official docs के लिए TanStack Virtual docs देखें।
इस article का flow आजमाने पर fixed height log viewer में mounted DOM rows बहुत कम हुए। Variable height chat में image load के बाद scroll थोड़ा jump हुआ, जब तक image size reserve और remeasure नहीं किया गया। सबसे useful checklist रही: real data सेestimateSizeadjust करना, 390px width test करना, बीच की known row तक scroll करना, और horizontal overflow न होने की पुष्टि करना।
मुफ़्त PDF: Claude Code cheatsheet
Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.
हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.
लेखक के बारे में
Masa
Claude Code workflow और team adoption पर काम करने वाला engineer.
संबंधित लेख
Claude Code permission safety ladder: access धीरे-धीरे बढ़ाएं
read-only से limited edits, proof commands और deploy checks तक permission बढ़ाने की सुरक्षित ladder.
Claude Code Small PR Proof Pack: छोटे PR को review-ready बनाना
Claude Code PR के लिए diff, checks, public URL, CTA path और rollback वाला practical proof pack.
Claude Code Review Gate Before Commit: diff, test, public URL और CTA जांच
Claude Code से commit से पहले review gate बनाएं: diff, build, public URL, Gumroad, consultation, tests और unrelated files।