Nested Tables HTML: What They Are and How to Fix Them
Find out why a table inside another table triggers a warning in your SEO report, and how to replace it with clean CSS Grid or Flexbox.
Free to use 10 min read Updated Oct 2026
Nested tables HTML: the quick answer
Quick answer: A nested table is a table element placed inside a cell of another table. It is valid HTML, but it is a weak way to build page layout, and our report raises a warning whenever it finds one on your rendered page.
- Pass: No table on the rendered page contains another table, so the Nested Tables row turns green and shows the message "This webpage is not using nested tables".
- Warning: At least one table contains another table, at any depth, and the row turns amber.
- Fix: Rebuild the layout with CSS Grid or Flexbox and keep only flat tables for real data.
We found no Google Search Central guidance that penalizes nesting tables. The harm is indirect: heavier markup, rendering that can drag on weak phones, an awkward reading order for screen readers, and stiff mobile layouts.
What are nested tables in HTML?
In HTML, nesting means a table within a table, where an inner table element sits inside a td or th cell of an outer one. The markup is legal because cells accept any flow content. The standard does not allow a table inside a caption, so a cell is the only real host. Here is a nested table example, which is the pattern the audit flags.
<table>
<tr>
<td>Sidebar</td>
<td>
<table>
<tr><td>Article text</td></tr>
</table>
</td>
</tr>
</table>A table inside a table is two levels, and one more inside that makes three. Our report counts differently, as the testing section explains.
One parser detail saves confusion. If you write a table straight inside another table or inside a tr, the browser pulls it out while it builds the page. Only tables within cells really nest, and that is the only form the audit can see.
Where nested tables come from
Most are inherited. The table based layout of the 1990s and 2000s put a table inside a table to build a header, a sidebar and a content column, often with a spacer gif holding the gaps open. WYSIWYG editor output from Dreamweaver and Microsoft FrontPage repeated that structure in bulk, and legacy HTML 4.01 templates were built around it. Tableless design with CSS later replaced the whole approach.
Pasted Word or Excel content, old newsletter templates and aging CMS themes carry it on, and embedded widgets can inject nested tables into a modern page. Framesets belong to the same legacy family, and our framesets guide covers that sibling check.
Why nested tables are bad for SEO, speed and accessibility
Start with the honest frame. We know of no Google documentation that punishes nested tables, and we won't invent a rule. The cost arrives through three side doors: render time, usability on a phone, and how clearly a reader or an assistive tool can follow your content.
Heavier markup and slower rendering
Every table adds a table element, a tr for each row and a td for each cell, and browsers also insert a tbody you never typed. Nesting multiplies that, so the Document Object Model fills with extra nodes and the HTML file size grows with them. Lighthouse flags an excessive DOM size for that reason, and our DOM size check measures that growth directly.
There is a layout cost too. Under the default auto table layout, the browser measures cell content before it can settle column widths, and a table inside a cell must be measured first. A fixed table layout avoids much of that, but old pages rarely use it. On a very large page viewed on a low end phone that extra rendering performance cost can add up, but how much depends on the size and depth of the nesting, and our check does not measure it.
Reading order for screen readers
Assistive technology such as JAWS, NVDA and VoiceOver works through a table row by row in source order when it reads in normal linear mode. When a cell holds another table, the screen reader works through that entire inner table before it reaches the next cell of the outer one. A layout that looks tidy on screen can therefore be read out in a confusing sequence. Data tables with proper th header cells let screen reader users move cell by cell and hear the matching header, and a nested table inside one breaks that link, so keep data tables flat.
That is the problem behind WCAG 1.3.2, Meaningful Sequence. The W3C documents it as WCAG layout table failure F49, which describes layout tables that stop making sense once the content is read in a straight line. A nested layout whose source order still reads sensibly does not fail F49, but you only find that out by testing it, which is why flattening is the safer route.
Mobile layouts and maintenance
Table layouts often hard-code pixel widths with width attributes on the table and its cells, and set spacing through cellpadding and cellspacing. Fixed widths overflow a narrow screen, and a media query has to fight the table model to restack cells. One small edit to an inner cell can also shift the whole grid.
How SEO Site Checker tests for nested tables
Our SEO audit loads your URL in headless Chromium and takes the rendered DOM as it stands after the page load event. Tables that scripts, widgets or embeds add before then are included, while tables injected much later may be missed.
The exact rule: The audit finds every table element and flags each table that contains at least one other table anywhere below it, at any depth, even when a div sits between them. The number we report is the count of containing tables, not inner tables.
Two examples make the count concrete. One outer table holding two inner tables gives 1, because only one table contains others. A stack of three gives 2, and a table inside a div inside a cell still counts.
| Result | Containing tables | Row in the report |
|---|---|---|
| Pass | 0 | Green, counts as a passed check |
| Warning | 1 or more | Amber, never a hard fail |
- Pass: No table contains another.
- Warning: At least one table contains another.
- Not tested: Rendering time, ARIA roles, layout versus data tables and email templates.
The row lives in the Advance section of the report. There is no depth threshold, so one level of nesting is enough. A warning is not counted as a pass, which means it lowers your score a little, because the score is passed checks divided by all counted checks. The row shows an amber warning icon beside a red How to Fix button, but the result stays a warning.
What the warning does and does not tell you
The warning points at structure worth a second look. That is all. The check does not time your rendering, does not read role or ARIA attributes, does not look at email templates and cannot tell a layout table from a data table. A legitimate nested data table raises the same warning, and a pass does not prove your tables are accessible. The report also does not print how many tables triggered the row, so the next section shows a Console line that normally reproduces the number.
How to find nested tables on your page
Hunting down nested tables HTML on a live page takes minutes with nothing but a browser. These steps take you from the warning row to the exact markup.
- Confirm the warning. Run the free report and open the Nested Tables row in the Advance section to see whether it warns.
- Count the containers. Open Chrome DevTools, switch to the Console and paste the first line below. It normally returns the report number, though the Console reads the live DOM, so late tables can shift it slightly.
- List the inner tables. Run the second line to list every inner table, or search the Elements panel for
table tableand step through each hit. - Check the source. On a static page, view source in the browser and search for an opening table tag. Tables added by JavaScript exist only in the live DOM.
// Tables that contain another table (matches the report)
[...document.querySelectorAll('table')].filter(t => t.querySelector('table')).length
// The inner tables themselves
document.querySelectorAll('table table')If you only have a snippet of markup, paste it into the online HTML viewer and inspect how the structure renders.
How to fix nested tables with CSS Grid and Flexbox
The fix for nested tables HTML is a rebuild, not a patch, and it goes faster table by table. Our HTML editor is handy for trying the new markup before the live site changes.
- Sort the tables. Decide for each one whether it is data, meaning related values in rows and columns, or layout, meaning boxes placed side by side. Only layout tables have to go.
- Replace the outer table. Use a container with
display: gridandgrid-template-columns, and turn each cell into a div or a semantic HTML element such asheader,main,asideorfooter. - Replace the inner table. Use a flex row with
display: flex, thegapproperty andalign-itemsfor things like an icon beside a line of text. - Flatten merged data. If the design needs merged cells, use
colspanandrowspanon one flat table instead of a table inside a cell. - Move the old attributes. Shift
width,bgcolor,alignandcellpaddinginto your stylesheet, then delete the empty wrapper elements. Our inline CSS guide helps with that move. - Test the result. Run the report again. The Nested Tables row should turn green and show the message "This webpage is not using nested tables".
Here is the before and after. The old version nests a table in the sidebar cell, and the new one does the same job with a wrapper, a nav and a main element plus a few lines of CSS. The MDN CSS Grid layout guide documents every property used below.
<!-- Before: a table inside a table -->
<table>
<tr>
<td>
<table><tr><td>Home</td><td>Blog</td></tr></table>
</td>
<td>Main content</td>
</tr>
</table><!-- After: a grid container -->
<div class="page">
<nav>Home | Blog</nav>
<main>Main content</main>
</div>.page { display: grid; grid-template-columns: 220px 1fr; gap: 24px; }
@media (max-width: 640px) { .page { grid-template-columns: 1fr; } }On a phone the grid needs one short rule to swap two columns for one, where a table layout means overriding the display of every td.
WordPress, Shopify and page builders
On WordPress, look first in the theme template files, in Custom HTML blocks, and in older posts with pasted content. The Gutenberg table block produces a flat table, so it is rarely the culprit. On Shopify, check the theme section files. In a page builder, swap the table for the builder's own row and column element, or paste the grid markup above into a custom code widget.
For widgets that inject their own tables, change the settings or remove the widget, because you cannot edit that markup.
When a table is still the right choice
Do not over correct. A table is the right element for tabular content, and the goal is to keep each one flat.
- Keep real data in one table. Prices, specs and schedules belong in a single flat table. Give it a
captionandthcells with ascopeattribute, so screen readers can announce which header applies to each cell. - Prefer spans to nesting. Reach for
colspanandrowspanbefore you put a table in a cell. - Follow the standard. The HTML spec on table layout rules out tables as a layout tool and names grid, flexbox, CSS multi-column layout and positioning as the alternatives.
- Know the email exception. HTML email is the main place table layouts survive, because clients such as Microsoft Outlook render CSS unevenly.
How to Fix
The short version, the same steps you see when you click How to Fix in your report.
Why this fails: At least one table on the rendered page contains another table, usually because of a table based layout, an old editor or an embedded widget. The extra markup can slow rendering on weak phones, scramble the reading order for screen readers and make mobile layouts stiff. It is a warning, not a ranking penalty.
How to fix it:
- Find the tables. Open the browser Console and run the line below. It counts tables that contain another table and normally matches the report, though tables injected late can make it differ.
- Decide whether each table holds data or only positions content.
- Rebuild layout tables with CSS Grid for the outer frame and Flexbox for inner rows.
- Flatten data tables with colspan and rowspan instead of putting a table inside a cell.
- Move width, bgcolor, align and cellpadding attributes into your stylesheet.
[...document.querySelectorAll('table')].filter(t => t.querySelector('table')).length<!-- Before -->
<table>
<tr>
<td>
<table><tr><td>Menu</td></tr></table>
</td>
<td>Content</td>
</tr>
</table>
<!-- After -->
<div class="page"><nav>Menu</nav><main>Content</main></div>.page { display: grid; grid-template-columns: 220px 1fr; gap: 24px; }Where to look on common platforms:
- WordPress: check the theme template files and any Custom HTML block.
- Shopify: check the theme section files.
- Page builders: swap the table for a row and column element.
- Embedded widgets: change or remove the widget, since you cannot edit its markup.
Re-test: Re-run the free report and look for the green Nested Tables row and its message, "This webpage is not using nested tables". If it still warns, look for tables injected by scripts or embeds, because the audit reads the rendered page.
Frequently Asked Questions
What is a nested table in HTML?
Can HTML tables be nested?
Are nested tables bad for SEO?
Do nested tables slow down page loading?
How do I replace nested tables with CSS?
Are nested tables accessible to screen readers?
Is it ever okay to use nested tables?
See how your site scores right now
Paste your address into the free report, then use the How to Fix button on anything that needs work.
Free to use. Results in under a minute for most sites.