TanStack Table Components Lab
Laboratorio frontend headless sobre TanStack Table v9: render manual 100% propio, orden multicolumna, filtrado combinado, paginación, selección con export CSV, columnas dinámicas, pinning + resize, agrupación con agregación y windowing manual de 5.000 filas. Vanilla JS, sin framework, sin build.
La Tabla que Decide: Datos Sin Framework
Cualquier tabla crece: más columnas, más filas, orden combinado, filtros que se acumulan. Los componentes de tabla con render incluido resuelven rápido, pero te casan con su DOM, sus clases y sus límites de performance.
TanStack Table invierte el trato: el core calcula el modelo (filas ordenadas, filtradas, agregadas) y el DOM es tu decisión. El desafío de este laboratorio fue consumir esa API headless desde vanilla JS puro — sin React, sin adapter, sin build — manteniendo accesibilidad y escape de contenido bajo control propio.
Sin arquitectura
- Plugins de tabla que deciden el HTML por ti
- Re-render completo al cambiar cualquier cosa
- Sort y filtro reinventados por proyecto
- innerHTML con datos: XSS por descuido
- Miles de filas = DOM inmanejable
Headless Total, DOM Propio
Headless Total
constructTable() + store.subscribe(): el core v9 expone el modelo, el render es 100% manual con createElement/textContent.
Features Tree-shakables
tableFeatures() arma el core a la carta: solo sorting en una tabla, sorting + filtros en otra. Cada feature se registra con su row model.
Render Manual Seguro
Cero innerHTML con datos: builders propios de thead/tbody/tfoot y esc() documentado. Los datos incluyen caracteres especiales como prueba viva.
Escalable a 5k Filas
Windowing manual sobre el modelo del core: el DOM pinta solo la ventana visible (throttle rAF), el estado siempre sostiene las 5.000. El bonus del lab lo implementa en vivo con métricas de nodos renderizados.
Nueve Componentes, Un Core
01 · Tabla Base Headless
constructTable + render manual
Core mínimo (coreRowModel) con 7 columnas y 500 registros deterministas. El DOM lo construye el módulo: thead/tbody/tfoot con createElement y textContent — sin innerHTML con datos. Footer con suma de horas del modelo. Render por slice de 25 filas; el modelo siempre opera sobre las 500.
—
Ver datos en tabla plana
| Proyecto | Tarea | Responsable | Horas | Fecha | Estado | Prioridad |
|---|---|---|---|---|---|---|
| Intranet Corporativa | QA funcional | María González | 3,5 h | 28-08-2026 | Completada | Media |
| Migración de Datos | Optimización de consultas | Carlos Rojas | 6,0 h | 26-08-2026 | En curso | Alta |
| Portal Clientes | Revisión de código | Ana Martín | 1,5 h | 24-08-2026 | Pendiente | Baja |
| Integración ERP | Pruebas de integración | Diego Torres | 4,5 h | 21-08-2026 | Bloqueada | Alta |
| App Móvil Ventas | Auditoría de accesibilidad "WCAG 2.2" | Lucía Herrera | 2,0 h | 19-08-2026 | Completada | Media |
| Sistema de Facturación | Refactor módulo <auth> & sesión | Javier Moreno | 8,0 h | 17-08-2026 | En curso | Alta |
02 · Orden Multicolumna
rowSortingFeature
Headers clicables con getToggleSortingHandler(): clic cicla asc → desc → limpiar; Shift + clic apila una segunda columna (indicador ·n = posición en el orden). aria-sort en cada th, sortFn semántico para estado y prioridad, fecha ISO ordenada cronológica.
Ver datos en tabla plana
| Proyecto | Responsable | Horas | Fecha | Estado |
|---|---|---|---|---|
| Intranet Corporativa | María González | 3,5 h | 28-08-2026 | Completada |
| Migración de Datos | Carlos Rojas | 6,0 h | 26-08-2026 | En curso |
| Portal Clientes | Ana Martín | 1,5 h | 24-08-2026 | Pendiente |
| Integración ERP | Diego Torres | 4,5 h | 21-08-2026 | Bloqueada |
| Mesa de Ayuda | Javier Moreno | 8,0 h | 17-08-2026 | En curso |
03 · Filtrado Combinado
globalFiltering + columnFiltering
Búsqueda global (todas las columnas, entiende etiquetas ES: "bloqueada" encuentra bloqueada) + filtro por columna estado y proyecto, combinados con AND. El contador X de 500 refleja el modelo filtrado real; la suma del footer se recalcula con las filas visibles.
Ver datos en tabla plana
| Proyecto | Tarea | Responsable | Horas | Estado |
|---|---|---|---|---|
| Rediseño Web Corporativa | Levantamiento de eventos | Elena Castro | 2,5 h | Pendiente |
| Integración ERP | Alerta de monitoreo: umbral > 90% | Paula Núñez | 1,0 h | Completada |
| Sistema de Facturación | Script de validación "post-deploy" | Luis Fernández | 3,0 h | Bloqueada |
| Migración de Datos | Mapeo de campos origen → destino | Carmen Silva | 5,5 h | En curso |
04 · Paginación
rowPaginationFeature
rowPaginationFeature + paginatedRowModel: el getRowModel devuelve la página activa y el modelo pre-paginación conserva las 500. Controles primera/anterior/siguiente/última, "Página X de Y", tamaño de página 10/25/50 y rango real "1–10 de 500". Combinada con orden multicolumna en los mismos headers.
Ver datos en tabla plana
| Página | Proyecto | Responsable | Horas | Estado |
|---|---|---|---|---|
| 1 | Intranet Corporativa | María González | 3,5 h | Completada |
| 2 | Migración de Datos | Carlos Rojas | 6,0 h | En curso |
| 3 | Portal Clientes | Ana Martín | 1,5 h | Pendiente |
05 · Selección + Export CSV
rowSelectionFeature
Checkbox por fila y de cabecera con estado indeterminate (parcial). La selección vive en el modelo (no en el DOM): "todos" marca las 500 aunque el render muestre el slice. Con N>0 aparece la barra flotante: Exportar CSV genera el archivo client-side con Blob — BOM UTF-8, delimitador ; y decimales coma para Excel en español — y Limpiar resetea el estado.
Ejemplo de CSV exportado (formato ES)
Proyecto;Tarea;Responsable;Horas;Fecha;Estado;Prioridad Intranet Corporativa;QA funcional;María González;3,5;28-08-2026;Completada;Media Sistema de Facturación;Refactor módulo <auth> & sesión;Javier Moreno;8,0;17-08-2026;En curso;Alta
06 · Columnas Dinámicas
columnVisibility + columnOrdering
columnVisibilityFeature + columnOrderingFeature: el panel oculta/muestra columnas con checkboxes y las reordena con los botones ←/→ o arrastrando el item (drag nativo). La tabla reacciona en vivo — headers, celdas y footer siguen el orden y la visibilidad del estado, con contador "X/7 columnas".
Ver datos en tabla plana
| Proyecto | Horas | Estado |
|---|---|---|
| Intranet Corporativa | 3,5 h | Completada |
| Integración ERP | 4,5 h | Bloqueada |
| Mesa de Ayuda | 8,0 h | En curso |
07 · Pinning + Resizing
columnPinning + columnResizing
columnPinningFeature fija Proyecto al inicio (sticky con sombra sutil que marca el borde flotante) y columnResizingFeature añade handles de arrastre en cada header. Los anchos viven en el estado (columnSizing) y el render los aplica en cada pasada; doble clic en el handle restablece el ancho original.
Ver datos en tabla plana
| Proyecto (fijada) | Tarea | Horas | Estado |
|---|---|---|---|
| Intranet Corporativa | QA funcional | 3,5 h | Completada |
| Migración de Datos | Optimización de consultas | 6,0 h | En curso |
| Portal Clientes | Revisión de código | 1,5 h | Pendiente |
08 · Grouping + Agregación
columnGrouping + rowAggregation
columnGroupingFeature + groupedRowModel + rowAggregationFeature: filas grupo colapsables "▸ Nombre (N)" con Σ horas agregadas (aggregationFn: 'sum' en la columna). El toggle alterna la agrupación entre Proyecto y Responsable; expandir/colapsar todo opera sobre rowExpandingFeature; los headers siguen ordenando (los grupos heredan el orden).
Ver agregados en tabla plana
| Proyecto | Registros | Σ Horas |
|---|---|---|
| App Móvil Ventas | 62 | Σ 214,5 h |
| Integración ERP | 59 | Σ 197,0 h |
| Mesa de Ayuda | 61 | Σ 203,5 h |
09 · Bonus: Virtualización
windowing manual · 5.000 filas
5.000 registros con windowing manual y cero dependencias extra: el core mantiene el modelo completo; el DOM pinta solo la ventana visible (± overscan) entre dos spacer rows que preservan la altura del scroll. El render se limita a un frame con requestAnimationFrame y la métrica viva muestra cuántas filas existen realmente en el DOM.
Cómo funciona el windowing
| Concepto | Implementación |
|---|---|
| Altura total | spacer rows top/bottom (altura = filas fuera de ventana × 40px) |
| Ventana | floor(scrollTop/40) − overscan … ceil((scrollTop+alto)/40) + overscan |
| Throttle | requestAnimationFrame: máximo un render por frame |
El Lab en Números
Componentes interactivos
Filas virtuales con windowing manual
Vendor ESM (table-core + store, sin build)
Dependencias de build
Herramientas del Laboratorio
Núcleo
Features
Patrones
Accesibilidad
¿Necesitas tablas que escalen sin perder el control?
Construí componentes headless sobre TanStack Table: orden, filtrado y agregación con render propio, accesible y sin dependencias de build.