Skip to main content

HTML Nesting Rules: Forms in Tables and Other Common Mistakes

Created: November 25, 2017 Larry Qu 4 min read

Introduction

HTML has strict rules about which elements can be nested — leading to broken layouts, JavaScript errors, and accessibility issues. This guide covers the most common nesting mistakes and how to fix them.

The Form-in-Table Problem

The Rule

A <form> element cannot span multiple table rows. It must be completely contained within a single <td> element.

Why It Breaks

HTML parserng form submissions to fail.

Wrong: Form Spanning Table Rows

<!-- WRONG: form is inside <tr> but outside <td> -->
<table>
  <tbody>
    <tr>
      <td>Product Name</td>
      <td>¥32.00</td>

      <!-- form starts here, inside <tr> but not inside <td> -->
      <form action="/carts/169" method="post">
        <td>
          <input type="number" name="cart[amount]" value="7">
          <input type="submit" value="Update">
        </td>
      </form>

      <td><a href="/carts/169" data-method="delete">Delete</a></td>
    </tr>
  </tbody>
</table>

What happens: The browser moves the <form> outside the <table>, breaking the layout. Firefox shows the invalid HTML highlighted in red in the source view.

Correct: Form Completely Inside a <td>

<!-- CORRECT: form is completely inside a <td> -->
<table>
  <tbody>
    <tr>
      <td>Product Name</td>
      <td>¥32.00</td>
      <td>
        <!-- form is entirely within this <td> -->
        <fo
          <input type="number" name="cart[amount]" value="7" min="1" max="100">
          <button type="submit" class="btn btn-primary btn-sm">Update</button>
        </form>
      </td>
      <td>
        <a href="/carts/169" data-method="delete" class="btn btn-danger btn-sm">Delete</a>
      </td>
    </tr>
  </tbody>
</table>

Alternative: Use div Layout Instead of Table

For shopping carts and product lists, a flexbox or grid layout is often cleaner than a table:


  <div class="product-row">
    <span class="product-name">Product Name</span>
    <span class="product-price">¥32.00</span>
ources

- [MDN: HTML Content Categories](https://developer.mozilla.org/en-US/docs/Web/HTML/Content_categories)
- [W3C HTML Validator](https://validator.w3.org/)
- [HTML Spec: The form element](https://html.spec.whatwg.org/multipage/form-elements.html#the-form-element)
- [Forms in Tables (Jukka Korpela)](https://www.cs.tut.fi/~jkorpela/forms/tables.html)
nsole shows warnings for some nesting errors.

### W3C Validator

Online validator

https://validator.w3.org/

CLI validator

npm install -g html-validate html-validate index.html


### In-Browser Check

// Check for HTML parsing errors in the browser const parser = new DOMParser(); const doc = parser.parseFromString(htmlString, ’text/html’); const errors = doc.querySelectorAll(‘parsererror’); if (errors.length > 0) { console.error(‘HTML parsing errors:’, errors); }

## Rests, text | Other `<a>` elements, `<button>` |
| `<button>` | Inline elements, text | `<a>`, `<input>`, `<button>` |
| `<ul>`, `<ol>` | `<li>` elements | Direct text, other elements |
| `<table>` | `<thead>`, `<tbody>`, `<tfoot>`, `<caption>` | Direct `<tr>` or `<td>` |
| `<tr>` | `<td>`, `<th>` | Direct text, other elements |
| `<form>` | Block and inline elements | Another `<form>` |

## Validating Your HTML

### Browser DevTools

Firefox highlights invalid HTML in red in the source view. Chrome's DevTools co<table> -->
<table>
  <td>Cell</td>
</table>

<!-- CORRECT: <td> must be inside <tr>, which must be inside <tbody>/<thead> -->
<table>
  <tbody>
    <tr>
      <td>Cell</td>
    </tr>
  </tbody>
</table>

HTML Content Model Reference

Understanding content models helps avoid nesting mistakes:

Element Can Contain Cannot Contain
<p> Inline elements, text Block elements (<div>, <p>, <ul>)
<span> Inline elements, text Block elements
<a> Inline elemen button inside a link –>

Click me


### List Items Outside Lists
  • Item 1
  • Item 2
    • Item 1
    • Item 2
    ```

    Table Structure Violations

    <!-- WRONG: <td> directly inside lid</div>
    </span>
    
    <!-- CORRECT: use block inside block, or inline inside inline -->
    <div>
      <div>This is valid</div>
    </div>
    
    <span>
      <em>This is valid</em>
    </span>
    

    Paragraphs Inside Paragraphs

    <!-- WRONG: <p> cannot contain block elements -->
    <p>
      Some text
      <div>A div inside a paragraph</div>
      More text
    </p>
    
    <!-- CORRECT: use separate paragraphs or a div -->
    <p>Some text</p>
    <div>A div</div>
    <p>More text</p>
    

    Interactive Elements Inside Interactive Elements

    <!-- WRONG: (button clicks have no effect), the cause is often invalid HTML nesting. Turbolinks is stricter about HTML validity than a regular page load.
    
    **Symptoms:**
    - Button click does nothing
    - No network request in DevTools
    - Works after disabling Turbolinks
    
    **Fix:** Ensure the form is completely inside a `<td>`, not spanning `<tr>` boundaries.
    
    ## Other Common HTML Nesting Mistakes
    
    ### Block Elements Inside Inline Elements
    
    ```html
    <!-- WRONG: <div> (block) inside <span> (inline) -->
    <span>
      <div>This is inva    <form action="/carts/169" method="post" class="quantity-form">
          <input type="number" name="cart[amount]" value="7" min="1" max="100">
          <button type="submit">Update</button>
        </form>
        <a href="/carts/169" data-method="delete">Delete</a>
      </div>
    </div>
    

    If you’re using Rails with Turbolinks and a form inside a table isn’t working

    Resources

    Comments

    Share this article

    Scan to read on mobile

    👍 Was this article helpful?