React - Data Table Sorting & Filtering
Overview
Estimated time: 20–30 minutes
Create a small data table with column sorting, text filtering, and accessible sort indicators.
Try it: Sort and filter
View source
function DataTable(){
const rows = React.useMemo(()=>[
{id:1, name:'Alice', role:'Admin'},
{id:2, name:'Bob', role:'User'},
{id:3, name:'Carol', role:'User'},
{id:4, name:'Dave', role:'Moderator'},
], []);
const [q, setQ] = React.useState('');
const [sort, setSort] = React.useState({key:'name', dir:'asc'});
const filtered = rows.filter(r => (r.name+r.role).toLowerCase().includes(q.toLowerCase()));
const sorted = [...filtered].sort((a,b)=>{
const av = a[sort.key].toString().toLowerCase();
const bv = b[sort.key].toString().toLowerCase();
return sort.dir==='asc' ? av.localeCompare(bv) : bv.localeCompare(av);
});
function toggle(key){ setSort(s => ({ key, dir: s.key===key && s.dir==='asc' ? 'desc' : 'asc'})); }
function Icon({k}){ return {sort.key===k ? (sort.dir==='asc'?' ▲':' ▼') : ''}; }
return (
<div>
<label>Filter <input value={q} onChange={e => setQ(e.target.value)} /></label>
<table role="table" style={{width:'100%', marginTop:8}}>
<thead><tr>
<th role="columnheader" scope="col"><button onClick={()=>toggle('name')} aria-sort={sort.key==='name'? sort.dir:'none'}>Name<Icon k="name" /></button></th>
<th role="columnheader" scope="col"><button onClick={()=>toggle('role')} aria-sort={sort.key==='role'? sort.dir:'none'}>Role<Icon k="role" /></button></th>
</tr></thead>
<tbody>
{sorted.map(r => <tr key={r.id}><td>{r.name}</td><td>{r.role}</td></tr>)}
</tbody>
</table>
</div>
);
}
ReactDOM.createRoot(document.getElementById('try-table')).render(<DataTable />);
Syntax primer
- aria-sort communicates the current sort order to assistive tech.
- Use stable sort keys and avoid in-place mutation.
Common pitfalls
- Sorting numbers as strings—normalize values before compare.
Exercises
- Add multi-column sort and a clear sort button.
Checks for Understanding
- What does
aria-sort
communicate, and where should it live? - Why copy arrays before sorting/filtering instead of mutating?
Show answers
- The current sort direction for a column; apply it to the interactive column header.
- To keep state immutable so React can detect changes and avoid subtle bugs.