NEWVersion 1.0 is here.
OpenTailwindv1.0
BlocksTemplatesCollectionsGuidesPricing
Tailwind CSS Table: Responsive, Striped and Sticky Examples
BlogTailwind CSS Table: Responsive, Striped and Sticky Examples
TutorialOctober 27, 2026·7 min read

Tailwind CSS Table: Responsive, Striped and Sticky Examples

Style HTML tables with Tailwind CSS: responsive horizontal scroll, striped rows, sticky headers, dark mode, number alignment and stacked mobile rows, with a live demo.

By OpenTailwind, by lindo.ai

On this page

  1. A responsive table with a sticky header
  2. A basic table
  3. Striped rows and hover states
  4. Sticky header and first column
  5. Stacked rows on phones
  6. Dark mode
  7. Comparison tables for pricing and features
  8. Table checklist
  9. FAQ

Quick answer: wrap the <table> in a div with overflow-x-auto so it scrolls sideways on small screens, give cells padding with px-4 py-3, separate rows with divide-y on <tbody>, and stripe them with even:bg-neutral-50. For long tables, add sticky top-0 to <thead> and set a maximum height on the wrapper.

Tailwind does not ship table components, and its base styles remove most browser table styling, so every table starts plain. This guide covers the classes that matter, a working demo with a sticky header, accessibility markup, dark mode, and the stacked layout for phones. At the end are pricing and feature comparison tables from the OpenTailwind library.

A responsive table with a sticky header

Original OpenTailwind educational example. Open the full-width demo.
Complete HTML, CSS, and JavaScript

Use your compiled Tailwind stylesheet in place of /examples/tailwind.css. The example uses Tailwind CSS 4 utilities and vanilla JavaScript.

<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex,follow"><title>A responsive table with a sticky header</title><link rel="stylesheet" href="/examples/tailwind.css"><style>body{font-family:system-ui,sans-serif;background:#faf9f6;color:#252824;padding:24px}button{cursor:pointer}button:disabled{cursor:wait;opacity:.6}:focus-visible{outline:3px solid #4f46e5;outline-offset:4px}</style></head><body><div class="max-h-72 overflow-auto rounded-2xl border border-stone-200 bg-white">
  <table class="w-full min-w-[36rem] border-collapse text-left text-sm">
    <caption class="sr-only">Sample orders</caption>
    <thead class="sticky top-0 bg-stone-100 text-stone-700">
      <tr>
        <th scope="col" class="px-4 py-3 font-semibold">Order</th>
        <th scope="col" class="px-4 py-3 font-semibold">Customer</th>
        <th scope="col" class="px-4 py-3 font-semibold">Status</th>
        <th scope="col" class="px-4 py-3 text-right font-semibold">Total</th>
      </tr>
    </thead>
    <tbody class="divide-y divide-stone-200">
      <tr class="even:bg-stone-50"><th scope="row" class="px-4 py-3 font-medium">#1042</th><td class="px-4 py-3">Ana Ruiz</td><td class="px-4 py-3">Paid</td><td class="px-4 py-3 text-right tabular-nums">$120.00</td></tr>
      <tr class="even:bg-stone-50"><th scope="row" class="px-4 py-3 font-medium">#1043</th><td class="px-4 py-3">Sam Lee</td><td class="px-4 py-3">Pending</td><td class="px-4 py-3 text-right tabular-nums">$48.50</td></tr>
      <tr class="even:bg-stone-50"><th scope="row" class="px-4 py-3 font-medium">#1044</th><td class="px-4 py-3">Priya Shah</td><td class="px-4 py-3">Refunded</td><td class="px-4 py-3 text-right tabular-nums">$75.00</td></tr>
      <tr class="even:bg-stone-50"><th scope="row" class="px-4 py-3 font-medium">#1045</th><td class="px-4 py-3">Jonas Berg</td><td class="px-4 py-3">Paid</td><td class="px-4 py-3 text-right tabular-nums">$310.25</td></tr>
      <tr class="even:bg-stone-50"><th scope="row" class="px-4 py-3 font-medium">#1046</th><td class="px-4 py-3">Mei Chen</td><td class="px-4 py-3">Paid</td><td class="px-4 py-3 text-right tabular-nums">$19.00</td></tr>
      <tr class="even:bg-stone-50"><th scope="row" class="px-4 py-3 font-medium">#1047</th><td class="px-4 py-3">Omar Haddad</td><td class="px-4 py-3">Pending</td><td class="px-4 py-3 text-right tabular-nums">$64.80</td></tr>
      <tr class="even:bg-stone-50"><th scope="row" class="px-4 py-3 font-medium">#1048</th><td class="px-4 py-3">Lena Novak</td><td class="px-4 py-3">Paid</td><td class="px-4 py-3 text-right tabular-nums">$92.10</td></tr>
    </tbody>
  </table>
</div>
<p class="mt-3 text-sm text-stone-600">Sample data. Scroll inside the table; narrow the window to scroll sideways.</p></body></html>

Scroll inside the table and the header stays in place. Narrow the window below about 576 pixels and the table scrolls sideways instead of squeezing the columns.

A basic table

<div class="overflow-x-auto rounded-xl border border-neutral-200">
  <table class="w-full min-w-[36rem] text-left text-sm">
    <caption class="sr-only">Recent orders</caption>
    <thead class="bg-neutral-50 text-neutral-700">
      <tr>
        <th scope="col" class="px-4 py-3 font-semibold">Order</th>
        <th scope="col" class="px-4 py-3 font-semibold">Customer</th>
        <th scope="col" class="px-4 py-3 text-right font-semibold">Total</th>
      </tr>
    </thead>
    <tbody class="divide-y divide-neutral-200">
      <tr>
        <th scope="row" class="px-4 py-3 font-medium">#1042</th>
        <td class="px-4 py-3">Ana Ruiz</td>
        <td class="px-4 py-3 text-right tabular-nums">$120.00</td>
      </tr>
    </tbody>
  </table>
</div>
  • overflow-x-auto on the wrapper, not the table. Tables do not scroll on their own.
  • min-w-[36rem] stops the columns from collapsing into unreadable widths before scrolling starts. Pick a width that fits your content.
  • <caption> names the table for screen readers. Keep it visible if it helps everyone, or use sr-only if a visible heading already says the same thing.
  • scope="col" and scope="row" tell assistive technology which headers describe each cell.
  • text-right tabular-nums right-aligns numbers and makes every digit the same width, so values line up by decimal place.

Striped rows and hover states

<tbody class="divide-y divide-neutral-200">
  <tr class="even:bg-neutral-50 hover:bg-indigo-50/60">…</tr>
</tbody>

even: and odd: are built-in Tailwind variants. Stripes help on wide tables where the eye can lose its row; on short tables, borders alone are usually enough. Keep the hover color subtle, and only add it if rows are clickable or the table is wide.

If a whole row links somewhere, put a real link in the first cell and let it cover the row with an after:absolute after:inset-0 pseudo-element on a relative row. A click handler on <tr> alone is not reachable by keyboard.

Sticky header and first column

<div class="max-h-96 overflow-auto">
  <table class="w-full border-separate border-spacing-0">
    <thead class="sticky top-0 z-10 bg-white">
      <tr><th class="border-b border-neutral-200 px-4 py-3">…</th></tr>
    </thead>
    <tbody>
      <tr>
        <th scope="row" class="sticky left-0 bg-white border-b border-neutral-200 px-4 py-3">…</th>
        <td class="border-b border-neutral-200 px-4 py-3">…</td>
      </tr>
    </tbody>
  </table>
</div>
  • The wrapper needs a height limit (max-h-96) and overflow-auto for a sticky header to have something to stick to.
  • Sticky cells need a background color, or rows will show through them.
  • With border-collapse, borders on sticky cells can scroll away from the cell in some browsers. Use border-separate border-spacing-0 and put borders on the cells, as above.

Stacked rows on phones

Horizontal scrolling works well for data tables. For short tables with few columns, such as a schedule or a plan summary, you can stack each row into a card below the sm breakpoint:

<table class="w-full text-sm">
  <thead class="max-sm:sr-only">…</thead>
  <tbody>
    <tr class="block border-b py-3 sm:table-row sm:py-0">
      <td data-label="Day" class="flex justify-between gap-4 py-1 sm:table-cell sm:px-4 sm:py-3
                                  max-sm:before:font-semibold max-sm:before:content-[attr(data-label)]">Monday</td>
      …
    </tr>
  </tbody>
</table>

Changing a table's display value can remove its table semantics in some browsers, so screen readers may no longer announce headers. If you stack rows, test with a screen reader, or add role="table", role="row" and role="cell" back explicitly. For data people need to compare, prefer horizontal scrolling.

Dark mode

<div class="overflow-x-auto rounded-xl border border-neutral-200 dark:border-neutral-800">
  <table class="w-full text-left text-sm text-neutral-700 dark:text-neutral-300">
    <thead class="bg-neutral-50 dark:bg-neutral-900">…</thead>
    <tbody class="divide-y divide-neutral-200 dark:divide-neutral-800">
      <tr class="even:bg-neutral-50 dark:even:bg-neutral-900/50">…</tr>
    </tbody>
  </table>
</div>

Stripes need much less contrast in dark mode than in light mode. Check that the header text and any status badges still meet contrast requirements in both themes. The color palette cheat sheet lists dark mode pairs for each shade.

Comparison tables for pricing and features

On marketing pages, the most common table is a plan comparison. A few rules make them easier to read:

  • Put plan names in the header row and features in the first column as row headers.
  • Use text, not only icons, for yes and no: a check mark with <span class="sr-only">Included</span>, and a dash with "Not included".
  • Group long feature lists under subheadings with a row that spans all columns (colspan).
  • Keep the plan names visible with a sticky header when the table is long.

Pricing comparison table

Pricing section with plan cards and a feature comparison table
Plan cards with a detailed comparison below. Preview

Feature comparison table

Feature comparison table with check marks across columns
Compare your product with alternatives feature by feature. Preview

Also see the Fresh pricing comparison table and all pricing blocks. Copying the code requires an account; selected blocks are free and Pro unlocks the full library.

Table checklist

  • Use a real <table> for tabular data, not a grid of divs.
  • Headers use <th> with scope, and the table has a caption or a heading that names it.
  • The wrapper scrolls horizontally on small screens, and the scroll area is keyboard reachable if it can receive focus.
  • Numbers are right-aligned with tabular-nums.
  • Status is not shown by color alone.
  • Sticky headers and columns have a background color.

FAQ

Why does my Tailwind table have no borders or spacing?

Tailwind's base styles reset table borders and set border-collapse: collapse. Add padding to cells and borders with border, divide-y or border-b on rows or cells.

How do I make a table scroll horizontally in Tailwind?

Wrap it in <div class="overflow-x-auto"> and give the table a minimum width, such as min-w-[36rem] or min-w-max. If the scroll area holds no focusable elements, add tabindex="0", a role of region and an aria-label to the wrapper so keyboard users can scroll it.

How do I set column widths?

Add table-fixed to the table and width classes such as w-1/3 or w-40 to the header cells. With the default table-auto, the browser sizes columns from their content.

How do I truncate long text in a cell?

Use max-w-xs truncate on the cell, or on an inner element, and put the full text in a title attribute or a details view. Truncation works best with table-fixed.

Related: the grid layout guide covers card layouts for content that does not need a table, and the pricing page tutorial builds the plan cards that usually sit above a comparison table.

Library access: Tutorials and their educational demos are public. Copying library designs requires an account: 100 selected blocks and five templates per industry are free. Pro lifetime access unlocks the complete library.

Keep building

How to Build a Pricing Page with Tailwind CSS (Monthly/Yearly Toggle) ↗

Build a responsive pricing page with a working monthly/yearly toggle using only Tailwind CSS and vanilla JavaScript. Includes accessibility and dark mode support.

Tailwind CSS Grid: A Practical Layout Guide for v4 ↗

Learn Tailwind CSS grid in v4: columns, spans, auto-fit responsive grids, bento layouts, subgrid and container queries, with copy-paste HTML examples.

Browse 1,761 Tailwind CSS blocks

100 free blocks, five free templates per industry, and Pro lifetime access to the complete block and template library.

Browse BlocksTemplates
← Previous article

Tailwind CSS Tabs: Accessible Examples with Keyboard Support

Next article →

Tailwind CSS Accessibility Checklist for v4 Projects

OpenTailwindv1.0

A little less starting from scratch.
A lot more making it yours.

Made with care by lindo.ai

The library

All blocksAll templatesHero sectionsPricing sections

Around here

Our storyGuides & ideasDesign examplesMembership & pricingQuestions?

Good design is a
good place to start.

FREE TO EXPLORE. YOURS TO BUILD.
© 2026 OpenTailwind · by lindo.ai
TermsPrivacyCookies All creativity welcome