TanStack Table vs Simple Table: When to Choose Headless vs Batteries-Included

ComparisonArchitecture GuideBest Practices

Should you build your own UI with TanStack Table or get started instantly with Simple Table? This guide breaks down the headless vs batteries-included debate with real-world examples.

When choosing a React data grid, you'll encounter two fundamentally different philosophies: headless libraries like TanStack Table that provide logic without UI, and batteries-included solutions like Simple Table that come with everything out of the box.

Both TanStack Table and Simple Table are excellent, MIT-licensed options with strong TypeScript support (both have generous free tiers). But they serve different needs and come with very different development experiences. Choosing the wrong approach can cost you weeks of development time or lock you into an architecture that doesn't fit your needs. For a broader overview, check out our guide to the best React table libraries in 2025.

This guide will help you understand when to choose each approach, with real code comparisons, bundle size analysis, and practical decision criteria.

Core Philosophy: The Fundamental Difference

Headless (TanStack Table)

"We provide the brain, you provide the body."

  • Provides hooks and state management logic
  • Zero opinions about rendering or styles
  • You write all the JSX and CSS
  • Complete control over every pixel

Analogy: Like buying a car engine and building the car around it yourself. Maximum flexibility, maximum effort.

Batteries-Included (Simple Table)

"We provide a complete working table. Just configure and go."

  • Provides a complete React component
  • Built-in UI with sensible defaults
  • Pass props, get a working table
  • Customizable via props, themes, renderers

Analogy: Like buying a complete car. It works immediately, but you can still customize paint, wheels, and interior.

Code Comparison: Building the Same Table

Let's build the exact same table with both libraries: a sortable, filterable table with pagination. Here's how much code each approach requires.

TanStack Table (Headless)

// ~100 lines of code for basic features
import { useReactTable, getCoreRowModel, getSortedRowModel,
  getFilteredRowModel, getPaginationRowModel, flexRender } from '@tanstack/react-table'
import { useState } from 'react'

export function TanStackTableExample({ data, columns }) {
  const [sorting, setSorting] = useState([])
  const [filtering, setFiltering] = useState('')
  const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 })

  const table = useReactTable({
    data,
    columns,
    state: { sorting, globalFilter: filtering, pagination },
    onSortingChange: setSorting,
    onGlobalFilterChange: setFiltering,
    onPaginationChange: setPagination,
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(),
    getFilteredRowModel: getFilteredRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
  })

  return (
    <div>
      {/* You build all of this UI from scratch */}
      <input
        value={filtering}
        onChange={(e) => setFiltering(e.target.value)}
        placeholder="Search..."
        className="border p-2 mb-4"
      />
      
      <table className="border-collapse border">
        <thead>
          {table.getHeaderGroups().map(headerGroup => (
            <tr key={headerGroup.id}>
              {headerGroup.headers.map(header => (
                <th
                  key={header.id}
                  onClick={header.column.getToggleSortingHandler()}
                  className="border p-2 cursor-pointer"
                >
                  {flexRender(header.column.columnDef.header, header.getContext())}
                  {header.column.getIsSorted() ? (
                    header.column.getIsSorted() === 'asc' ? ' ↑' : ' ↓'
                  ) : ''}
                </th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody>
          {table.getRowModel().rows.map(row => (
            <tr key={row.id}>
              {row.getVisibleCells().map(cell => (
                <td key={cell.id} className="border p-2">
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>

      {/* Pagination UI - you build this too */}
      <div className="flex gap-2 mt-4">
        <button
          onClick={() => table.previousPage()}
          disabled={!table.getCanPreviousPage()}
          className="px-4 py-2 border"
        >
          Previous
        </button>
        <span className="px-4 py-2">
          Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}
        </span>
        <button
          onClick={() => table.nextPage()}
          disabled={!table.getCanNextPage()}
          className="px-4 py-2 border"
        >
          Next
        </button>
      </div>
    </div>
  )
}

// Plus: Need to define column configuration, styling, etc.

Reality check: This is a simplified example. A production-ready table with proper styling, accessibility, loading states, and virtualization would be 300-500+ lines.

Simple Table (Batteries-Included)

// ~15 lines of code for the same features
import { SimpleTable } from 'simple-table-core'
import 'simple-table-core/styles.css'

export function SimpleTableExample({ data, columns }) {
  return (
    <SimpleTable
      rows={data}
      defaultHeaders={columns}
      rowIdAccessor="id"
      height={400}
    />
  )
}

// That's it. Everything else is built-in.
// Sorting, filtering, pagination, virtualization, accessibility - all included by default.

Instant productivity: You get a production-ready table with sorting, filtering, pagination, virtualization, keyboard navigation, and accessibility built-in. Zero extra code required.

Bundle Size & Performance Reality Check

TanStack Table

Core Library
15.2 kB
But you also need to add:
  • + Your table UI code (~5-10KB)
  • + Virtualization library (~3-5KB)
  • + Styling/CSS (~2-5KB)
  • + Custom components (~5-15KB)
Real-world total: ~30-40KB

Simple Table

Complete Package
32 kB
Everything included:
  • ✓ Complete table UI
  • ✓ Built-in virtualization
  • ✓ Styling & themes
  • ✓ All components
  • ✓ Accessibility features
Real-world total: 32 kB

The Bundle Size Paradox

TanStack Table's core is lighter, but when you add the UI code you need to write, plus virtualization libraries and styling, your total bundle is often larger than Simple Table's complete package. Simple Table is optimized as a single, cohesive bundle with tree-shaking. See our comprehensive bundle size comparison for detailed analysis.

Development Speed: Time to First Table

TanStack Table Timeline

1
Day 1-2: Learning
Read docs, understand hooks, study examples
2
Day 3-5: Building UI
Create table components, style them, add interactions
3
Day 6-8: Polish
Add virtualization, accessibility, loading states
Time to production-ready table:
1-2 weeks

Simple Table Timeline

1
Hour 1: Install & Basic Table
npm install, import, pass data. Working table.
2
Hour 2-3: Add Features
Enable sorting, filtering, pagination with props
3
Hour 4: Customize
Apply theme, add custom renderers if needed
Time to production-ready table:
4 hours

Cost Calculation (Senior Dev @ $100/hr)

TanStack Table:
80 hours × $100 = $8,000
Simple Table:
4 hours × $100 = $400
Time savings: ~$7,600 per project

The Tradeoff: Flexibility vs Convenience

When TanStack Table Wins

1. Ultra-Custom UI Requirements

Need a table that doesn't look like a table? Cards, timeline views, kanban boards? TanStack lets you use table logic for non-table UIs.

2. Framework Portability

TanStack works with React, Vue, Svelte, Solid. If you need the same logic across frameworks, headless makes sense.

3. Existing Design System

If you have a strict design system with existing components, building on TanStack ensures perfect visual consistency.

4. Team Expertise

Experienced team that enjoys building UI from scratch? TanStack gives them full creative control.

When Simple Table Wins

1. Speed to Market

Startup, MVP, tight deadline? Get a production-ready table in hours, not weeks. Ship features, not infrastructure.

2. Standard Table UIs

Need a table that looks like... a table? Simple Table's defaults are polished, accessible, and production-ready.

3. Small Teams / Solo Devs

Don't have time to build and maintain UI components? Simple Table lets you focus on business logic, not table internals.

4. Performance-Critical Apps

Smallest bundle + built-in virtualization = fastest load times. Perfect for mobile-first or performance-sensitive apps.

Customization: Myth vs Reality

Common Myth: "TanStack Table gives you unlimited customization. Simple Table locks you into their design."

Reality: Both are highly customizable—they just use different approaches.

TanStack Table Customization

  • Complete control: Write any JSX, any styles
  • Custom everything: Headers, cells, footers, loading states
  • But: You write 300+ lines for what Simple Table does in 10
  • Maintenance: Every customization is code you now maintain

Simple Table Customization

  • CSS Variables: Change colors, spacing, borders instantly
  • Custom Renderers: Replace any cell/header with your component
  • Themes: Dark mode, custom themes out of the box
  • Prop-based: Most customization is just changing props

The Real Question

Ask yourself: "Do I need to customize the UI structure itself, or just colors/spacing/content?"

• If structure: TanStack gives you JSX control
• If styling/content: Simple Table's renderers + CSS variables are faster

Learn more: Customizing React tables with themes | Custom renderers docs

Decision Framework: Which Should You Choose?

Choose TanStack Table When:

  • You need to build non-table UIs (cards, lists, kanban) with table logic
  • Your design system requires pixel-perfect custom components that don't fit standard table patterns
  • You need to support multiple frameworks (React, Vue, Svelte) with shared logic
  • Your team enjoys building UI and wants full creative control
  • You have 1-2 weeks to build a polished table from scratch

Choose Simple Table When:

  • You need a standard data table (which is 90% of use cases)
  • You want to ship fast—hours, not weeks
  • You're a small team or solo developer without time to build UI infrastructure
  • Performance matters—you need the smallest bundle + built-in virtualization
  • You want production-ready defaults with easy customization via props/themes
  • You need enterprise features (row grouping, aggregation, pinning) without paying AG Grid fees

The Verdict: Start with Simple Table, Graduate to TanStack if Needed

Here's the honest advice based on working with both libraries in production:

For 90% of Projects: Start with Simple Table

Unless you have a specific reason to build UI from scratch (like truly custom layouts or multi-framework support), Simple Table will get you to production faster with less code and a smaller bundle.

Reality check: Most teams who choose TanStack initially think they need maximum flexibility, but 6 months later they've built... a standard table that looks like Simple Table's defaults. Save yourself the time.

For 10% of Projects: TanStack Table Shines

If you truly need non-standard layouts, multi-framework support, or you're building a design system from scratch, TanStack Table is the right tool. Just be honest about whether you need that level of control.

Tip: You can always migrate from Simple Table to TanStack later if you hit a wall. But you probably won't.

Skip the UI busywork. Start with Simple Table's FREE tier.

Get a production-ready table in hours, not weeks. Built-in virtualization, sorting, filtering, grouping, and themes—all in 28KB. FREE tier includes all core features. Optional PRO ($85/mo) for priority support. Save ~$7,600 in dev time per project vs building on TanStack from scratch.