Laboratorio Frontend

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.

Tabla del TanStack Table Components Lab con encabezados ordenables, badges de estado, footer con suma de horas, panel flotante de columnas y métrica de virtualización de 17 nodos sobre 5.000 filas
El Contexto

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
La Solución

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.

Showcase Interactivo

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.

Tabla base headless: registro de operación interna con proyecto, tarea, responsable, horas, fecha, estado y prioridad

—

Ver datos en tabla plana
ProyectoTareaResponsableHorasFechaEstadoPrioridad
Intranet CorporativaQA funcionalMaría González3,5 h28-08-2026CompletadaMedia
Migración de DatosOptimización de consultasCarlos Rojas6,0 h26-08-2026En cursoAlta
Portal ClientesRevisión de códigoAna Martín1,5 h24-08-2026PendienteBaja
Integración ERPPruebas de integraciónDiego Torres4,5 h21-08-2026BloqueadaAlta
App Móvil VentasAuditoría de accesibilidad "WCAG 2.2"Lucía Herrera2,0 h19-08-2026CompletadaMedia
Sistema de FacturaciónRefactor módulo <auth> & sesiónJavier Moreno8,0 h17-08-2026En cursoAlta

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.

—
Tabla con orden multicolumna: clic ordena, Shift más clic apila columnas
Ver datos en tabla plana
ProyectoResponsableHorasFechaEstado
Intranet CorporativaMaría González3,5 h28-08-2026Completada
Migración de DatosCarlos Rojas6,0 h26-08-2026En curso
Portal ClientesAna Martín1,5 h24-08-2026Pendiente
Integración ERPDiego Torres4,5 h21-08-2026Bloqueada
Mesa de AyudaJavier Moreno8,0 h17-08-2026En 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.

— de 500
Tabla con filtrado combinado: búsqueda global más filtros de estado y proyecto
Ver datos en tabla plana
ProyectoTareaResponsableHorasEstado
Rediseño Web CorporativaLevantamiento de eventosElena Castro2,5 hPendiente
Integración ERPAlerta de monitoreo: umbral > 90%Paula Núñez1,0 hCompletada
Sistema de FacturaciónScript de validación "post-deploy"Luis Fernández3,0 hBloqueada
Migración de DatosMapeo de campos origen → destinoCarmen Silva5,5 hEn 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.

Página 1 de 50
—
Tabla paginada: controles de página y tamaño de página combinados con orden por columnas
Ver datos en tabla plana
PáginaProyectoResponsableHorasEstado
1Intranet CorporativaMaría González3,5 hCompletada
2Migración de DatosCarlos Rojas6,0 hEn curso
3Portal ClientesAna Martín1,5 hPendiente

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.

Marca filas con los checkboxes: la barra de acciones aparece sobre la tabla. —
Tabla con selección de filas por checkbox y exportación a CSV
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".

—
Tabla con columnas dinámicas: visibilidad y orden controlados desde el panel
Ver datos en tabla plana
ProyectoHorasEstado
Intranet Corporativa3,5 hCompletada
Integración ERP4,5 hBloqueada
Mesa de Ayuda8,0 hEn 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.

Arrastra los bordes del header para redimensionar · doble clic restablece
Tabla con columna Proyecto fijada y columnas redimensionables arrastrando el header
Ver datos en tabla plana
Proyecto (fijada)TareaHorasEstado
Intranet CorporativaQA funcional3,5 hCompletada
Migración de DatosOptimización de consultas6,0 hEn curso
Portal ClientesRevisión de código1,5 hPendiente

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).

—
Tabla agrupada por proyecto con filas de grupo colapsables y suma de horas agregada
Ver agregados en tabla plana
ProyectoRegistrosΣ Horas
App Móvil Ventas62Σ 214,5 h
Integración ERP59Σ 197,0 h
Mesa de Ayuda61Σ 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.

Haz scroll: la barra de métricas muestra las filas renderizadas en tiempo real. —
Tabla virtualizada con cinco mil filas: solo se renderiza la ventana visible del scroll
Cómo funciona el windowing
ConceptoImplementación
Altura totalspacer rows top/bottom (altura = filas fuera de ventana × 40px)
Ventanafloor(scrollTop/40) − overscan … ceil((scrollTop+alto)/40) + overscan
ThrottlerequestAnimationFrame: máximo un render por frame
Métricas

El Lab en Números

9

Componentes interactivos

5.000

Filas virtuales con windowing manual

118,5 KB

Vendor ESM (table-core + store, sin build)

0

Dependencias de build

Stack Tecnológico

Herramientas del Laboratorio

Núcleo

@tanstack/table-core 9.2.4 @tanstack/store 0.11.1 JavaScript ES6+ HTML5 CSS3

Features

rowSortingFeature globalFilteringFeature rowPaginationFeature rowSelectionFeature columnVisibilityFeature columnPinningFeature columnGroupingFeature rowAggregationFeature

Patrones

Headless Store subscribe Render manual Windowing + rAF Export CSV ES Vendored ESM 0 dependencias de build

Accesibilidad

aria-sort caption focus-visible Tabla fallback Graceful degradation

¿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.