Referencia

Exports por subpath, diagnósticos, atajos de teclado, preferencias de vista guardadas, imágenes optimizadas y el resto de opciones.

Diagnósticos

listkit emite diagnósticos con código para que una config rota aparezca en desarrollo y no en producción. Los LK1xxx lanzan en dev (no-op en prod); los LK2xxx avisan una vez; los LK3xxx se muestran en la UI.

CódigoSeveridadSignificado
LK1001errorKey de campo duplicada en el universo exportable.
LK1002errorExport solicitado sin configuración de export.
LK1003errorLa petición traía una key fuera del whitelist (se descarta).
LK1004errorSort sobre un path que resuelve a array — precomputa un campo plano.
LK2001warnUn campo produjo un valor no primitivo; la celda queda vacía.
LK2002warnCelda sobre el límite de 32,767 caracteres de Excel; truncada.
LK2003warndata: URI descartada de una celda (URLs planas sí pasan).
LK2004warnPetición GET demasiado grande — cambia el resolver a POST.
LK3001infomaxRows truncó el export — se muestra en el diálogo ("N de M filas").

Estado vacío

"Sin resultados" significa cosas distintas: una lista a la que nadie ha escrito quiere una invitación, una filtrada quiere una pista para aflojar los filtros. Compónlo con empty, sin reemplazar el layout:

import { PackageOpen } from 'lucide-react'

defineListConfig<Product>({
	empty: {
		title: 'Aún no hay productos',
		message: 'Agrega tu primer producto para verlo aquí.',
		icon: <PackageOpen size={40} />,
		action: <button onClick={openCreate}>Nuevo producto</button>,
	},
})
CampoEfecto
titleEncabezado. Por defecto usa la etiqueta empty activa.
messageLínea de apoyo bajo el título.
iconGlifo propio. null quita el bloque del ícono — más denso para una lista embebida.
actionUn botón o enlace, para que la pantalla vacía tenga un siguiente paso.

emptyMessage es el atajo de una línea y empty lo sobreescribe; renderEmpty reemplaza el bloque completo y es el último recurso — usa empty primero para que el espaciado y el tema sigan siendo consistentes con el resto de la lista.

Atajos de teclado

Activos por defecto. Cada atajo se enlaza por capacidad, no por estado: una lista con filtros siempre responde +, haya o no un filtro aplicado en este momento — así las teclas nunca se mueven bajo tus manos.

TeclasAcción
⌘ K / Ctrl KEnfocar la búsqueda
+Abrir el panel de filtros, en su buscador
-Quitar el último filtro aplicado
Shift + CLimpiar todos los filtros
Shift + VAlternar tabla / tarjetas
Shift + DAlternar filas compactas / amplias
Shift + EAbrir la exportación configurable
Shift + RRefrescar la lista
Shift + ASeleccionar la página actual
EscLimpiar la selección
/ Página anterior / siguiente
Shift + ← / Shift + →Primera / última página
?Mostrar esta lista, en un overlay

? abre el overlay de ayuda, que lista solo los atajos que esa lista realmente enlaza — lee el mismo registro que los handlers, así que no puede anunciar una tecla muerta. La pista de ? también vive en el menú de opciones, para quien no la descubra tecleando.

Los atajos nunca disparan mientras escribes en un input, textarea o contenteditable, así que - dentro de la búsqueda sigue siendo un guion. Cada uno se enlaza solo si la lista tiene la función: sin config de export, no hay Shift + E, y el overlay no lo lista.

Preferencias de vista guardadas

Las preferencias que describen cómo trabaja un usuario con una lista persisten por id de lista, para que la lista abra como la dejó. Lo que se guarda:

PreferenciaSe define desde
Orden de columnasGestor de columnas, arrastre
Columnas ocultasGestor de columnas
Ancho de columnasRedimensionar el encabezado
DensidadMenú de opciones
Vista (tabla/tarjetas)Toggle de vista
Filas por páginaSelector del footer
Barra de filtros rápidosMenú de opciones

Un parámetro de URL siempre le gana al valor guardado, así que un enlace compartido muestra la vista de quien lo mandó, no la de quien lo recibe.

La vista es la única preferencia que el dispositivo puede sobreescribir: una pantalla angosta abre en tarjetas sin importar lo guardado — una tabla no cabe — y un cambio hecho ahí no se guarda, así que revisar las columnas desde el teléfono nunca cambia cómo abre la lista en escritorio.

El almacenamiento es localStorage por defecto y es reemplazable — respáldalo con tu tabla de configuración de usuario para llevar las preferencias entre dispositivos:

import type { ColumnStorage } from 'listkit'

const dbColumnStorage: ColumnStorage = {
	get: key => cache.get(key) ?? null,
	set: (key, prefs) => {
		cache.set(key, prefs)
		void api.saveColumnPrefs(key, prefs)
	},
}

<ListView config={config} adapter={adapter} columnStorage={dbColumnStorage} />

get/set son síncronos para que la tabla pinte las columnas correctas en el primer frame. Para respaldarlo con un store asíncrono, hidrata un caché por adelantado (desde un valor renderizado en el servidor o un fetch único), haz que get lea ese caché y deja que set dispare la escritura en segundo plano — como arriba.

Imágenes optimizadas (ListImage)

Para tablas/tarjetas densas llenas de miniaturas, <ListImage> reserva su caja (sin layout shift), hace lazy-load y decodificación asíncrona, muestra un placeholder shimmer y cae en un fallback ante errores:

import { ListImage } from 'listkit'

{ key: 'photo', header: '', exportable: false,
  render: p => <ListImage src={p.photo} alt={p.name} width={40} height={40} /> }

En Next.js, inyecta el componente optimizado — React puro cae a <img>:

import Image from 'next/image'
;<ListImage as={Image} src={src} alt={alt} width={48} height={48} />

La compresión del lado del cliente pertenece al momento de subida (reducir antes de almacenar), no al renderizado — descargar una imagen completa solo para recomprimirla en JS hace el render más lento, no más rápido. Lazy-loading + optimización del framework es lo que acelera las listas con muchas imágenes.

Orden inicial (defaultSort)

defineListConfig<Order>({
	id: 'orders',
	defaultSort: { field: 'placedAt', dir: 'desc' },
	table: { columns: [{ key: 'placedAt', header: 'Fecha', sortable: true }] },
})

La lista abre ordenada, el encabezado muestra la flecha y desde ahí el usuario cicla el orden. Un sort ya presente en la URL gana, y limpiar el orden no se vuelve a aplicar hasta la siguiente carga. buildListQuery también lo aplica en servidor, así el seed de SSR coincide con la primera query del cliente.

Subpath Exports

Ruta de importaciónContenido
listkitListView, defineListConfig, ListKitProvider, ListSkeleton, invalidateListCache, adapters, hooks, primitives, types
listkit/nextuseNextRouterAdapter, NextListView
listkit/react-routeruseReactRouterAdapter
listkit/adaptersmemoryAdapter, fetchAdapter, serverActionAdapter, createDexieAdapter
listkit/serverbuildListQuery, loadInitialList, defineListConfig — seguro para RSC (sin React/DOM)
listkit/queryparseListkitQuery, parseSelectionDescriptor, filtersById, getString/getBoolean/getStringArray/getDateRange/getNumberRange/getText, paginate — parsear un request a ListQuery y leer sus filtros
listkit/sqlexecuteSqlList, buildSqlFilter, buildSearch, buildOrderBy, sqlPaginate, textCondition, sqlFieldMapFromFilters — fragmentos Postgres + ejecutor (inyección de pool, sin driver)
listkit/mongobuildMongoQuery, buildMongoFilter, buildMongoSort, mongoPaginate, combineFilters, escapeRegex, mongoFieldMapFromFilters, filterConfigToMongoFieldMaps — objetos de query de MongoDB (sin driver)
listkit/mongooseexecutePaginatedListkitQuery, executeAggregateListkitQuery, castFilterToSchema, resolveSelectionFilter — corre la query de página en Mongoose (peer dep mongoose opcional, type-only)
listkit/react-queryuseReactQueryListData, invalidateList, listQueryKey — respalda listas con TanStack Query
listkit/tailwind.cssRegistro de fuente Tailwind v4

Licencia

MIT © Ricardo Tapia

On this page