HTML table generator

Build clean, semantic table markup with accessible headers and a live preview — plus a starter stylesheet to make it look right.

HTML Table Generator

Build clean, semantic table markup — with a proper header row and accessible scope attributes — plus a starter stylesheet to make it look right.

Live Preview (markup + starter CSS, as it renders on a plain page)

Copy-Paste Output

HTML markup
Starter CSS

How to use this tool

Sighted readers understand a table instantly because they can see the grid. Screen reader users can't — they navigate cell by cell, and rely entirely on the markup to know what each value means.

That's what <thead>, <th> and the scope attribute provide. With them, moving through a table announces "Revenue, Q3: £48,000" — the header travels with the value. Without them, it announces "48,000" and the user has to remember which column they're in. On a wide table, that's the difference between usable and useless.

scope="col" marks a header for its column; scope="row" marks one for its row. Adding a <caption> is worth doing too — it gives the whole table a title that assistive technology announces on entry.

The layout-tables myth, and its modern inversion

In the 1990s, tables were the only reliable way to build page layouts, and the practice persisted long after CSS could do the job properly. The correction — "never use tables" — became received wisdom.

It over-corrected. The rule was always don't use tables for layout, not don't use tables. Today the more common mistake is the reverse: building tabular data out of divs and flexbox because tables feel outdated. That throws away every accessibility benefit above, plus keyboard navigation and the browser's own column alignment, and gains nothing — CSS can style a real <table> however you like.

The test is simple: if your content is rows and columns of related values, it is a table. Use one.

Making tables work on mobile

Wide tables are the genuine hard problem in responsive design. Three approaches work, depending on the data.

Horizontal scroll is the simplest and safest: wrap the table in a container with overflow-x: auto, keeping the structure intact. Add tabindex="0" to that wrapper so keyboard users can scroll it too — an easily missed accessibility detail. Stacked cards re-flow each row into a block with labels shown inline, which reads well on phones but needs the labels duplicating in CSS or data attributes. Column hiding shows only essential columns on narrow screens, which works when some columns are genuinely secondary.

Styling that aids comprehension

A few choices measurably improve how quickly people read a table. Zebra striping helps the eye track across long rows. Right-aligning numeric columns lines up the digits so magnitudes are comparable at a glance — a detail that separates a considered table from a default one. Generous cell padding beats tight borders for readability, and a sticky header row keeps context visible while scrolling long data sets.

Developer Implementation Guide: Rendering Tables from JSON

In modern web applications, you rarely write static HTML tables by hand. Instead, you receive data in JSON format from an API and need to render a table dynamically.

Dynamic Table Rendering in React

Here is a clean, reusable approach to rendering a data table in React. It accepts an array of objects and automatically generates the table headers based on the object keys.


export default function DynamicTable({ data }) {
  if (!data || data.length === 0) {
    return <p>No data available.</p>;
  }

  // Extract headers from the first object's keys
  const headers = Object.keys(data[0]);

  return (
    <table className="data-table">
      <thead>
        <tr>
          {headers.map(header => (
            <th key={header}>
              // Capitalize first letter
              {header.charAt(0).toUpperCase() + header.slice(1)}
            </th>
          ))}
        </tr>
      </thead>
      <tbody>
        {data.map((row, index) => (
          <tr key={index}>
            {headers.map(header => (
              <td key={${index}-}>{row[header]}</td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}
        

This component is highly scalable. To improve it further for production, you would typically add pagination, column sorting, and define custom renderers for specific column types (like formatting dates or currencies) rather than rendering the raw string value.

Frequently asked questions

Is it bad practice to use HTML tables?

Only for page layout. For genuine tabular data — rows and columns of related values — a real table element is the correct, accessible and SEO-friendly choice. Building tables out of divs discards accessibility for no benefit.

What does scope="col" do in a table?

It tells assistive technology that a th cell is the header for its column, so screen readers announce the header alongside each value — "Revenue, Q3: £48,000" instead of an unlabelled number. Use scope="row" for row headers.

How do I make an HTML table responsive?

The simplest reliable method is wrapping it in a container with overflow-x: auto so it scrolls horizontally with its structure intact. Add tabindex="0" to that wrapper so keyboard users can scroll it as well.

Should I add a caption to my table?

Yes where the table needs a title. A caption element gives the table an accessible name that screen readers announce on entry, providing context before the user starts navigating cells.