Expandable Rows Table Template

Keep your tables clean and scannable by hiding secondary information in expandable rows.

With a single click, users can reveal a hidden row containing more details, perfect for order summaries, user profiles, or any data with primary and secondary importance.

Screenshot of a table where one row is expanded to show more details underneath it.

Get Source Code Preview

About this Expandable Table

This "accordion" or "expandable row" pattern is a fantastic way to manage information density. The main table shows only the most critical data. A dedicated hidden row, placed directly after a main row in the HTML, contains additional details.

The JavaScript logic is simple:

  1. When a user clicks on a main row (specifically, one with the class .row-expandable), the script finds the very next row in the DOM.
  2. It then toggles a "visible" class on that next row, which changes its display property.
  3. A class is also toggled on the clicked row, which triggers a CSS transform on the + icon, rotating it 45 degrees to look like an x (the close symbol). This could easily be modified to switch between + and - if you prefer.

The expandable row itself contains a td with a colspan attribute, allowing its content to span the full width of the table, perfect for displaying free-form details.

Features

Ideal Use Cases

Code

Here's the full code, including the HTML structure, CSS for the effect, and JavaScript to manage the state:

x
 
<style>
.expandable-table {
    width: 100%;
    border-collapse: collapse;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
.expandable-table thead th {
    padding: 12px;
    background-color: #f2f2f2;
    text-align: left;
    font-weight: 600;
}
.expandable-table tbody td {
    padding: 12px;
    border-bottom: 1px solid #ddd;
}
/* This is the row the user clicks on */
.expandable-table .row-expandable {
    cursor: pointer;
    transition: background-color 0.2s;
}
.expandable-table .row-expandable:hover {
    background-color: #f8f9fa;
}
/* This is the row that is hidden/shown */
.expandable-table .row-details {
    display: none; /* Hidden by default */
}
.expandable-table .row-details.visible {
    display: table-row; /* Show it when it has the 'visible' class */
}
.expandable-table .row-details td {
    background-color: #f8f9fa;
    padding: 20px;
    border-bottom: 2px solid #ccc; /* A thicker border to separate it */
}
/* Styling for the expand/collapse icon */
.expandable-table .expand-icon {
    font-weight: bold;
    transition: transform 0.3s ease;
    display: inline-block;
}
.expandable-table .row-expandable.open .expand-icon {
    transform: rotate(45deg);
}
</style>
<table class="expandable-table" id="myExpandableTable">
    <thead>
        <tr>
            <th scope="col" style="width: 5%;"></th>
            <th scope="col">Order ID</th>
            <th scope="col">Date</th>
            <th scope="col">Total</th>
            <th scope="col">Status</th>
        </tr>
    </thead>
    <tbody>
        <!-- First Record -->
        <tr class="row-expandable">
            <td><span class="expand-icon">+</span></td>
            <td>#5501</td>
            <td>2024-08-20</td>
            <td>$199.50</td>
            <td>Delivered</td>
        </tr>
        <tr class="row-details">
            <td colspan="5">
                <div>
                    <strong>Items Purchased:</strong>
                    <ul>
                        <li>Product A (1) - $99.50</li>
                        <li>Product B (2) - $100.00</li>
                    </ul>
                    <strong>Shipping Address:</strong> 123 Main St, Anytown, USA
                </div>
            </td>
        </tr>
        
        <!-- Second Record -->
        <tr class="row-expandable">
            <td><span class="expand-icon">+</span></td>
            <td>#5502</td>
            <td>2024-08-21</td>
            <td>$45.00</td>
            <td>Shipped</td>
        </tr>
        <tr class="row-details">
            <td colspan="5">
                <div>
                    <strong>Items Purchased:</strong>
                    <ul>
                        <li>Product C (1) - $45.00</li>
                    </ul>
                    <strong>Shipping Address:</strong> 456 Oak Ave, Somecity, USA
                </div>
            </td>
        </tr>
        
        <!-- Third Record -->
         <tr class="row-expandable">
            <td><span class="expand-icon">+</span></td>
            <td>#5503</td>
            <td>2024-08-22</td>
            <td>$88.75</td>
            <td>Processing</td>
        </tr>
        <tr class="row-details">
            <td colspan="5">
                <div>
                    <strong>Items Purchased:</strong>
                    <ul>
                        <li>Product D (3) - $75.00</li>
                        <li>Product E (1) - $13.75</li>
                    </ul>
                    <strong>Shipping Address:</strong> 789 Pine Ln, Otherville, USA
                </div>
            </td>
        </tr>
    </tbody>
</table>
<script>
/**
 * Enables expandable/collapsible rows for a table.
 * @param {string} tableId The ID of the table.
 */
function enableExpandableRows(tableId) {
    const table = document.getElementById(tableId);
    if (!table) return;
    table.addEventListener('click', function(e) {
        // Find the closest ancestor 'tr' that is expandable
        const expandableRow = e.target.closest('tr.row-expandable');
        if (!expandableRow) return;
        // Find the very next TR element, which is the details row
        const detailsRow = expandableRow.nextElementSibling;
        if (detailsRow && detailsRow.classList.contains('row-details')) {
            expandableRow.classList.toggle('open');
            detailsRow.classList.toggle('visible');
        }
    });
}
// Initialize the functionality
enableExpandableRows('myExpandableTable');
</script>

View Output