Component

Sortable Table

Employee directory table with interactive column sorting. Click any column header to sort data. Supports alphabetical, numeric, and custom data sorting with visual feedback.

  • Component
  • Tailwind
  • HTML
  • MIT

Preview

Live component

Interactive preview
Open preview
9:41 100%
devsnips.dev/library/tailwind/components/tables/sortable-table/
fluid · no fixed width 100%
No dependencies Production-ready

Install

Add to your project

terminal
npx devsnips add Tailwind/Components/Tables/sortable-table
registry path: Tailwind/Components/Tables/sortable-table

Source

Implementation

code.html
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-x-auto">
  <table class="w-full min-w-[640px]" id="sortableTable">
    <thead>
      <tr class="bg-gray-50 border-b border-gray-200">
        <th class="px-5 py-3.5 text-left">
          <button onclick="sortTable(0)" class="flex items-center gap-2 text-xs font-bold text-gray-600 uppercase tracking-wider hover:text-gray-900 group">
            Employee
            <svg class="w-4 h-4 text-gray-400 group-hover:text-gray-600 transition-colors sort-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"/>
            </svg>
          </button>
        </th>
        <th class="px-5 py-3.5 text-left">
          <button onclick="sortTable(1)" class="flex items-center gap-2 text-xs font-bold text-gray-600 uppercase tracking-wider hover:text-gray-900 group">
            Department
            <svg class="w-4 h-4 text-gray-400 group-hover:text-gray-600 transition-colors sort-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"/>
            </svg>
          </button>
        </th>
        <th class="px-5 py-3.5 text-left">
          <button onclick="sortTable(2)" class="flex items-center gap-2 text-xs font-bold text-gray-600 uppercase tracking-wider hover:text-gray-900 group">
            Salary
            <svg class="w-4 h-4 text-gray-400 group-hover:text-gray-600 transition-colors sort-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"/>
            </svg>
          </button>
        </th>
        <th class="px-5 py-3.5 text-left">
          <button onclick="sortTable(3)" class="flex items-center gap-2 text-xs font-bold text-gray-600 uppercase tracking-wider hover:text-gray-900 group">
            Performance
            <svg class="w-4 h-4 text-gray-400 group-hover:text-gray-600 transition-colors sort-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"/>
            </svg>
          </button>
        </th>
        <th class="px-5 py-3.5 text-left">
          <button onclick="sortTable(4)" class="flex items-center gap-2 text-xs font-bold text-gray-600 uppercase tracking-wider hover:text-gray-900 group">
            Experience
            <svg class="w-4 h-4 text-gray-400 group-hover:text-gray-600 transition-colors sort-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"/>
            </svg>
          </button>
        </th>
        <th class="px-5 py-3.5 text-left">
          <button onclick="sortTable(5)" class="flex items-center gap-2 text-xs font-bold text-gray-600 uppercase tracking-wider hover:text-gray-900 group">
            Status
            <svg class="w-4 h-4 text-gray-400 group-hover:text-gray-600 transition-colors sort-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"/>
            </svg>
          </button>
        </th>
      </tr>
    </thead>
    <tbody class="divide-y divide-gray-100" id="tableBody">
      <tr class="hover:bg-gray-50 transition-colors" data-salary="145000" data-performance="92" data-experience="8">
        <td class="px-5 py-4">
          <div class="flex items-center gap-3">
            <img src="https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=80&h=80&fit=crop" alt="James Wilson" class="w-10 h-10 rounded-full object-cover">
            <div>
              <p class="text-sm font-medium text-gray-900">James Wilson</p>
              <p class="text-xs text-gray-500">j.wilson@company.com</p>
            </div>
          </div>
        </td>
        <td class="px-5 py-4 text-sm text-gray-600">Engineering</td>
        <td class="px-5 py-4 text-sm font-semibold text-gray-900">$145,000</td>
        <td class="px-5 py-4">
          <div class="flex items-center gap-2">
            <div class="w-20 h-2 bg-gray-200 rounded-full overflow-hidden">
              <div class="h-full bg-emerald-500 rounded-full" style="width: 92%"></div>
            </div>
            <span class="text-sm font-medium text-emerald-600">92%</span>
          </div>
        </td>
        <td class="px-5 py-4 text-sm text-gray-600">8 years</td>
        <td class="px-5 py-4">
          <span class="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-emerald-100 text-emerald-700">Active</span>
        </td>
      </tr>
      <tr class="hover:bg-gray-50 transition-colors" data-salary="125000" data-performance="88" data-experience="5">
        <td class="px-5 py-4">
          <div class="flex items-center gap-3">
            <img src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=80&h=80&fit=crop" alt="Sarah Chen" class="w-10 h-10 rounded-full object-cover">
            <div>
              <p class="text-sm font-medium text-gray-900">Sarah Chen</p>
              <p class="text-xs text-gray-500">s.chen@company.com</p>
            </div>
          </div>
        </td>
        <td class="px-5 py-4 text-sm text-gray-600">Product</td>
        <td class="px-5 py-4 text-sm font-semibold text-gray-900">$125,000</td>
        <td class="px-5 py-4">
          <div class="flex items-center gap-2">
            <div class="w-20 h-2 bg-gray-200 rounded-full overflow-hidden">
              <div class="h-full bg-emerald-500 rounded-full" style="width: 88%"></div>
            </div>
            <span class="text-sm font-medium text-emerald-600">88%</span>
          </div>
        </td>
        <td class="px-5 py-4 text-sm text-gray-600">5 years</td>
        <td class="px-5 py-4">
          <span class="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-emerald-100 text-emerald-700">Active</span>
        </td>
      </tr>
      <tr class="hover:bg-gray-50 transition-colors" data-salary="95000" data-performance="76" data-experience="3">
        <td class="px-5 py-4">
          <div class="flex items-center gap-3">
            <img src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=80&h=80&fit=crop" alt="Michael Park" class="w-10 h-10 rounded-full object-cover">
            <div>
              <p class="text-sm font-medium text-gray-900">Michael Park</p>
              <p class="text-xs text-gray-500">m.park@company.com</p>
            </div>
          </div>
        </td>
        <td class="px-5 py-4 text-sm text-gray-600">Design</td>
        <td class="px-5 py-4 text-sm font-semibold text-gray-900">$95,000</td>
        <td class="px-5 py-4">
          <div class="flex items-center gap-2">
            <div class="w-20 h-2 bg-gray-200 rounded-full overflow-hidden">
              <div class="h-full bg-amber-500 rounded-full" style="width: 76%"></div>
            </div>
            <span class="text-sm font-medium text-amber-600">76%</span>
          </div>
        </td>
        <td class="px-5 py-4 text-sm text-gray-600">3 years</td>
        <td class="px-5 py-4">
          <span class="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-emerald-100 text-emerald-700">Active</span>
        </td>
      </tr>
      <tr class="hover:bg-gray-50 transition-colors" data-salary="110000" data-performance="84" data-experience="4">
        <td class="px-5 py-4">
          <div class="flex items-center gap-3">
            <img src="https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=80&h=80&fit=crop" alt="Emma Thompson" class="w-10 h-10 rounded-full object-cover">
            <div>
              <p class="text-sm font-medium text-gray-900">Emma Thompson</p>
              <p class="text-xs text-gray-500">e.thompson@company.com</p>
            </div>
          </div>
        </td>
        <td class="px-5 py-4 text-sm text-gray-600">Marketing</td>
        <td class="px-5 py-4 text-sm font-semibold text-gray-900">$110,000</td>
        <td class="px-5 py-4">
          <div class="flex items-center gap-2">
            <div class="w-20 h-2 bg-gray-200 rounded-full overflow-hidden">
              <div class="h-full bg-blue-500 rounded-full" style="width: 84%"></div>
            </div>
            <span class="text-sm font-medium text-blue-600">84%</span>
          </div>
        </td>
        <td class="px-5 py-4 text-sm text-gray-600">4 years</td>
        <td class="px-5 py-4">
          <span class="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-amber-100 text-amber-700">On Leave</span>
        </td>
      </tr>
      <tr class="hover:bg-gray-50 transition-colors" data-salary="135000" data-performance="90" data-experience="7">
        <td class="px-5 py-4">
          <div class="flex items-center gap-3">
            <img src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=80&h=80&fit=crop" alt="David Lee" class="w-10 h-10 rounded-full object-cover">
            <div>
              <p class="text-sm font-medium text-gray-900">David Lee</p>
              <p class="text-xs text-gray-500">d.lee@company.com</p>
            </div>
          </div>
        </td>
        <td class="px-5 py-4 text-sm text-gray-600">Engineering</td>
        <td class="px-5 py-4 text-sm font-semibold text-gray-900">$135,000</td>
        <td class="px-5 py-4">
          <div class="flex items-center gap-2">
            <div class="w-20 h-2 bg-gray-200 rounded-full overflow-hidden">
              <div class="h-full bg-emerald-500 rounded-full" style="width: 90%"></div>
            </div>
            <span class="text-sm font-medium text-emerald-600">90%</span>
          </div>
        </td>
        <td class="px-5 py-4 text-sm text-gray-600">7 years</td>
        <td class="px-5 py-4">
          <span class="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-emerald-100 text-emerald-700">Active</span>
        </td>
      </tr>
    </tbody>
  </table>
</div>

<script>
let sortDirection = {};
function sortTable(columnIndex) {
  const table = document.getElementById('sortableTable');
  const tbody = document.getElementById('tableBody');
  const rows = Array.from(tbody.querySelectorAll('tr'));
  
  sortDirection[columnIndex] = !sortDirection[columnIndex];
  const direction = sortDirection[columnIndex] ? 1 : -1;
  
  const sortedRows = rows.sort((a, b) => {
    let aVal, bVal;
    
    if (columnIndex === 0 || columnIndex === 1 || columnIndex === 5) {
      aVal = a.cells[columnIndex].textContent.trim();
      bVal = b.cells[columnIndex].textContent.trim();
      return aVal.localeCompare(bVal) * direction;
    } else if (columnIndex === 2) {
      aVal = parseInt(a.dataset.salary);
      bVal = parseInt(b.dataset.salary);
    } else if (columnIndex === 3) {
      aVal = parseInt(a.dataset.performance);
      bVal = parseInt(b.dataset.performance);
    } else if (columnIndex === 4) {
      aVal = parseInt(a.dataset.experience);
      bVal = parseInt(b.dataset.experience);
    }
    
    return (aVal - bVal) * direction;
  });
  
  tbody.innerHTML = '';
  sortedRows.forEach(row => tbody.appendChild(row));
  
  document.querySelectorAll('.sort-icon').forEach(icon => {
    icon.classList.remove('text-blue-600');
    icon.classList.add('text-gray-400');
  });
  
  const headerIcons = table.querySelectorAll('th button .sort-icon');
  headerIcons[columnIndex].classList.remove('text-gray-400');
  headerIcons[columnIndex].classList.add('text-blue-600');
}
</script>
228 lines UTF-8 · LF · Spaces: 2

AI-ready

Available to your agent.

This component is reachable through the DevSnips CLI, MCP server and Skills integrations — one registry, one resource path.

Resource path

devsnips://components/tables/sortable-table

Continue browsing

View all