Tips & Tricks (अपडेट: 2/6/2026)

Claude Code से React टेबल कंपोनेंट बनाएं: sort, filter और pagination

Claude Code से React table बनाएं: sorting, filtering, pagination, mobile, TanStack Table और Playwright checks.

Claude Code से React टेबल कंपोनेंट बनाएं: sort, filter और pagination

पहले table contract तय करें

Admin dashboard, customer list, billing history, product catalog और content analytics में table बार-बार आता है। शुरू में काम आसान लगता है: rows दिखा दो। लेकिन असली जरूरत जल्दी बढ़ती है: revenue के आधार पर sort करना, status से filter करना, लंबी list को pages में बांटना, mobile पर पढ़ने लायक बनाना, keyboard से चलाना और बदलाव के बाद Playwright से जांचना।

Claude Code इस काम को तेज कर सकता है, लेकिन prompt साफ होना चाहिए। अगर आप सिर्फ “एक अच्छी table बना दो” लिखेंगे, तो हो सकता है output में div grid हो, caption न हो, sort arrow दिखे लेकिन data sort न हो, या mobile view में column label गायब हो जाए। Table data relationship दिखाने वाला UI है, इसलिए HTML semantics, state, responsive behavior, accessibility और tests साथ में मांगना बेहतर है।

इस लेख में हम React/TypeScript का copy-pasteable table component बनाएंगे। इसमें semantic table, sortable columns, global filter, pagination, mobile CSS, accessibility review, TanStack Table कब चुनें, और Playwright checks शामिल हैं। React workflow के लिए Claude Code React development और accessibility के लिए Claude Code accessibility guide भी देखें।

Official references जरूर देखें: MDN <table>, MDN aria-sort, TanStack Table docs, Playwright Writing tests, और Claude Code overview

Semantic table basics

जब row और column मिलकर अर्थ बनाते हैं, तब table सही choice है। Customer name, plan, MRR, status और signup date tabular data हैं, क्योंकि हर value अपने column header से समझ में आती है। अगर items अलग-अलग cards हैं, तो list या card grid बेहतर हो सकती है।

Basic structure में caption, thead, tbody और th scope="col" रखें। अगर पहली cell row को identify करती है, तो th scope="row" इस्तेमाल करें।

<table>
  <caption>Customer के हिसाब से monthly recurring revenue</caption>
  <thead>
    <tr>
      <th scope="col">Customer</th>
      <th scope="col">Plan</th>
      <th scope="col">MRR</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Northwind</th>
      <td>Pro</td>
      <td>$1,200</td>
    </tr>
  </tbody>
</table>

Claude Code को prompt देते समय लिखें कि native table structure, caption और scope attributes बने रहने चाहिए। इससे visual grid तो मिलता ही है, साथ में browser और assistive technology भी data relation समझते हैं।

flowchart TD
  A["Requirements"] --> B["Semantic table"]
  B --> C["Sort, filter, pagination"]
  C --> D["Mobile layout"]
  D --> E["Accessibility review"]
  E --> F["Playwright checks"]

Claude Code prompt

Table component data, CSS, state और tests को जोड़ता है। इसलिए prompt में scope और ban list दोनों रखें।

React + TypeScript में customer table component बनाएं।

Requirements:
- केवल src/components/DataTable.tsx और src/components/data-table.css बदलें
- table, caption, thead, tbody और th scope इस्तेमाल करें
- Fields: id, name, plan, mrr, status, signedUpAt
- Global filter, column sorting और 5-row pagination जोड़ें
- aria-sort केवल currently sorted column पर लगाएं
- Column header के अंदर button से sorting करें
- Mobile पर data-label से cell labels दिखाएं
- Playwright test में filter, sort, pagination और mobile labels check करें

Do not:
- नई UI library add न करें
- role="grid" तब तक न लगाएं जब तक grid keyboard model implement न हो
- pseudocode न दें

इससे Claude Code को पता चलता है कि output सिर्फ सुंदर markup नहीं, बल्कि review-ready implementation होना चाहिए।

Copy-paste React/TypeScript implementation

यह implementation छोटी और मध्यम lists के लिए पर्याप्त है। इसमें filter बदलते समय page 1 पर वापस जाने का guard भी है।

// src/components/DataTable.tsx
"use client";

import { useMemo, useState, type ReactNode } from "react";
import "./data-table.css";

type SortDirection = "asc" | "desc";
type SortState<T> = { key: keyof T; direction: SortDirection } | null;

type Customer = {
  id: string;
  name: string;
  plan: "Free" | "Pro" | "Enterprise";
  mrr: number;
  status: "active" | "trial" | "paused";
  signedUpAt: string;
};

type Column<T> = {
  key: keyof T;
  label: string;
  numeric?: boolean;
  render?: (value: T[keyof T], row: T) => ReactNode;
};

const pageSize = 5;
const rows: Customer[] = [
  { id: "cus_001", name: "Northwind", plan: "Pro", mrr: 1200, status: "active", signedUpAt: "2026-01-15" },
  { id: "cus_002", name: "Blue Bottle", plan: "Free", mrr: 0, status: "trial", signedUpAt: "2026-02-02" },
  { id: "cus_003", name: "Kobayashi Studio", plan: "Enterprise", mrr: 8400, status: "active", signedUpAt: "2025-11-20" },
  { id: "cus_004", name: "Atlas Foods", plan: "Pro", mrr: 980, status: "paused", signedUpAt: "2025-12-09" },
  { id: "cus_005", name: "Green Lab", plan: "Pro", mrr: 1600, status: "active", signedUpAt: "2026-03-01" },
  { id: "cus_006", name: "Sakura Dental", plan: "Free", mrr: 0, status: "trial", signedUpAt: "2026-03-18" },
];

const money = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  maximumFractionDigits: 0,
});

const columns: Column<Customer>[] = [
  { key: "name", label: "Customer" },
  { key: "plan", label: "Plan" },
  { key: "mrr", label: "MRR", numeric: true, render: (_, row) => money.format(row.mrr) },
  { key: "status", label: "Status" },
  { key: "signedUpAt", label: "Signed up", render: (_, row) => new Date(row.signedUpAt).toLocaleDateString("en-US") },
];

function compare<T>(a: T, b: T, key: keyof T) {
  const left = a[key];
  const right = b[key];
  if (typeof left === "number" && typeof right === "number") return left - right;
  return String(left).localeCompare(String(right), undefined, { numeric: true, sensitivity: "base" });
}

export function DataTable() {
  const [query, setQuery] = useState("");
  const [page, setPage] = useState(1);
  const [sort, setSort] = useState<SortState<Customer>>({ key: "name", direction: "asc" });

  const filtered = useMemo(() => {
    const keyword = query.trim().toLowerCase();
    if (!keyword) return rows;
    return rows.filter((row) =>
      columns.some((column) => String(row[column.key]).toLowerCase().includes(keyword)),
    );
  }, [query]);

  const sorted = useMemo(() => {
    if (!sort) return filtered;
    return [...filtered].sort((a, b) => {
      const result = compare(a, b, sort.key);
      return sort.direction === "asc" ? result : -result;
    });
  }, [filtered, sort]);

  const totalPages = Math.max(1, Math.ceil(sorted.length / pageSize));
  const currentPage = Math.min(page, totalPages);
  const pageRows = sorted.slice((currentPage - 1) * pageSize, currentPage * pageSize);

  function updateQuery(value: string) {
    setQuery(value);
    setPage(1);
  }

  function toggleSort(key: keyof Customer) {
    setSort((current) => {
      if (!current || current.key !== key) return { key, direction: "asc" };
      return { key, direction: current.direction === "asc" ? "desc" : "asc" };
    });
  }

  return (
    <section className="table-shell" aria-labelledby="customers-title">
      <label>
        <span>Filter customers</span>
        <input value={query} onChange={(event) => updateQuery(event.target.value)} type="search" />
      </label>
      <div className="table-scroll" tabIndex={0}>
        <table className="data-table">
          <caption id="customers-title">Monthly recurring revenue by customer</caption>
          <thead>
            <tr>
              {columns.map((column) => {
                const isSorted = sort?.key === column.key;
                const ariaSort = isSorted ? (sort.direction === "asc" ? "ascending" : "descending") : undefined;
                return (
                  <th key={String(column.key)} scope="col" aria-sort={ariaSort} className={column.numeric ? "numeric" : undefined}>
                    <button type="button" onClick={() => toggleSort(column.key)}>{column.label}</button>
                  </th>
                );
              })}
            </tr>
          </thead>
          <tbody>
            {pageRows.map((row) => (
              <tr key={row.id}>
                {columns.map((column, index) => {
                  const content = column.render ? column.render(row[column.key], row) : String(row[column.key]);
                  return index === 0 ? (
                    <th key={String(column.key)} scope="row" data-label={column.label}>{content}</th>
                  ) : (
                    <td key={String(column.key)} data-label={column.label} className={column.numeric ? "numeric" : undefined}>{content}</td>
                  );
                })}
              </tr>
            ))}
          </tbody>
        </table>
      </div>
      <nav className="pagination" aria-label="Table pagination">
        <button type="button" disabled={currentPage === 1} onClick={() => setPage((value) => value - 1)}>Previous</button>
        <span aria-live="polite">Page {currentPage} of {totalPages}</span>
        <button type="button" disabled={currentPage === totalPages} onClick={() => setPage((value) => value + 1)}>Next</button>
      </nav>
    </section>
  );
}

Mobile CSS और accessibility

Narrow screen पर table को card-like layout में दिखा सकते हैं, लेकिन DOM में table structure बना रहना चाहिए।

.table-scroll {
  overflow-x: auto;
}

.data-table {
  width: 100%;
  border-collapse: collapse;
}

.data-table th,
.data-table td {
  border-top: 1px solid #e5e7eb;
  padding: 0.75rem;
  text-align: left;
}

.pagination {
  display: flex;
  gap: 0.75rem;
  justify-content: flex-end;
}

@media (max-width: 640px) {
  .data-table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
  }

  .data-table,
  .data-table tbody,
  .data-table tr,
  .data-table th,
  .data-table td {
    display: block;
    width: 100%;
  }

  .data-table th,
  .data-table td {
    display: grid;
    grid-template-columns: 8rem 1fr;
    gap: 0.75rem;
  }

  .data-table th::before,
  .data-table td::before {
    content: attr(data-label);
    font-weight: 700;
  }
}

Accessibility checklist में caption, scope, sorting button, aria-sort, filter label और pagination के aria-live को देखें। role="grid" तभी इस्तेमाल करें जब spreadsheet जैसा keyboard behavior भी implement कर रहे हों।

TanStack Table कब चुनें

Simple list के लिए custom component ठीक है। लेकिन column visibility, per-column filters, row selection, server-side pagination, pinned columns या virtualization चाहिए, तो TanStack Table बेहतर है। यह headless library है: logic देती है, UI आप अपने design system से बनाते हैं।

Use caseकौन सा तरीकाध्यान देने वाली बात
छोटी admin listCustom componentकम dependency, पर feature खुद maintain करना होगा
Complex dashboardTanStack TableAPI सीखनी होगी
Spreadsheet जैसा editorEnterprise gridbundle size और license देखें

Claude Code से कहें कि dependency जोड़ने से पहले कारण बताए। हर table को heavy grid की जरूरत नहीं होती।

Playwright checks

Table test में सिर्फ page load नहीं, workflow check करें।

// tests/customer-table.spec.ts
import { expect, test } from "@playwright/test";

test("customer table works", async ({ page }) => {
  await page.goto("/customers");
  await expect(page.getByRole("table", { name: /monthly recurring revenue/i })).toBeVisible();

  await page.getByRole("button", { name: /MRR/ }).click();
  await expect(page.getByRole("columnheader", { name: /MRR/ })).toHaveAttribute("aria-sort", "ascending");

  await page.getByLabel("Filter customers").fill("north");
  await expect(page.getByRole("row", { name: /Northwind/ })).toBeVisible();

  await page.getByLabel("Filter customers").fill("");
  await page.getByRole("button", { name: "Next" }).click();
  await expect(page.getByText("Page 2 of 2")).toBeVisible();

  await page.setViewportSize({ width: 390, height: 844 });
  await expect(page.locator("td[data-label='Plan']").first()).toBeVisible();
});

Claude Code को bug fix देते समय यह test भी दें और लिखें कि पहले failing case reproduce करे।

Use cases, pitfalls और monetization CTA

Use caseTable featuresValue
SaaS customer listPlan, MRR, status, renewal datechurn risk और upsell candidates दिखते हैं
Ecommerce catalogStock, price, category, publish statestock और price mistakes जल्दी मिलती हैं
Content dashboardPV, read rate, CTA clicks, update daterewrite priority और ad revenue decisions सुधरते हैं
Billing historyPayment status, amount, due datesupport समय घटता है

Common pitfalls हैं: div से fake table बनाना, sort arrow दिखाना पर aria-sort भूलना, filter पर page 1 न करना, mobile को अंत में जोड़ना, और Claude Code से unnecessary UI library install करा लेना।

Table monetization में मदद करती है क्योंकि next action साफ दिखता है। Content site में traffic, CTA clicks और article revenue साथ रखें। SaaS में MRR, usage drop और renewal date साथ रखें। Team workflow के लिए Claude Code training and consulting देखें। अकेले सीखने के लिए products and templates से शुरुआत करें।

Tested result

Masa ने यह pattern एक छोटी customer list में आजमाया। सबसे उपयोगी सुधार था filter बदलते ही page 1 पर लौटना; पहले page 2 पर search करने से result होने के बाद भी empty screen दिखती थी। दूसरा फायदा था शुरू से data-label जोड़ना, जिससे mobile CSS बाद में patch नहीं करना पड़ा। Claude Code को semantics, state, mobile, accessibility और Playwright एक साथ बताने पर review आसान हुआ।

#Claude Code #table #React #TanStack Table #UI
मुफ़्त

मुफ़्त PDF: Claude Code cheatsheet

Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.

हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.

Masa

लेखक के बारे में

Masa

Claude Code workflow और team adoption पर काम करने वाला engineer.