Docyrus

DataGallery

Card gallery for React Native — a TanStack-table-driven FlashList of rich cards (5 variants, 8 field slots, cover styles, density, responsive columns) with toolbar, search, selection bar and paging. API-aligned with the web DataGallery.

iOSAndroid
Preview DataGallery on your device

Scan with Expo Go

Download Expo Go, then scan the QR code to preview native components.

Installation

pnpm dlx @docyrus/cli add @docyrus/rn-data-gallery
Required Packages(3 packages)
pnpm add @tanstack/react-table @shopify/flash-list tailwind-variants

DataGallery renders the rows of a TanStack table as cards. useDataGallery owns the table, the display config and the search state; spread its result onto <DataGallery> and <DataGalleryToolbar>. The same card engine (DataGalleryCard) powers the data grid's gallery display mode and useDocyrusDataGallery for Docyrus data sources.

Usage

import { useState } from 'react';
import { View } from 'react-native';

import { type ColumnDef } from '@tanstack/react-table';

import {
  DataGallery,
  DataGalleryToolbar,
  useDataGallery,
  type DataGalleryCardConfigSerializable
} from '@/components/docyrus-native/data-gallery';

type Product = { id: string; name: string; category: string; price: number; image: string };

const columns: Array<ColumnDef<Product>> = [
  { id: 'name', accessorKey: 'name', header: 'Name' },
  { id: 'category', accessorKey: 'category', header: 'Category' },
  { id: 'price', accessorKey: 'price', header: 'Price', meta: { cell: { variant: 'currency', currency: 'USD' } } },
  { id: 'image', accessorKey: 'image', header: 'Image' }
];

export function ProductGallery({ products }: { products: Array<Product> }) {
  const [cardConfig, setCardConfig] = useState<DataGalleryCardConfigSerializable>({
    titleField: 'name',
    subtitleField: 'category',
    coverImageField: 'image',
    bodyFields: ['price'],
    variant: 'product'
  });
  const gallery = useDataGallery<Product>({
    data: products,
    columns,
    enableSearch: true,
    enableRowSelection: true,
    getRowId: row => row.id,
    displayConfig: { cardVariant: 'product', coverStyle: 'top-lg' }
  });

  return (
    <View className="flex-1">
      <DataGalleryToolbar
        table={gallery.table}
        displayConfig={gallery.displayConfig}
        onDisplayConfigChange={gallery.setDisplayConfig}
        cardConfig={cardConfig}
        onCardConfigChange={setCardConfig}
        enableCardConfig
        searchQuery={gallery.searchQuery}
        onSearchQueryChange={gallery.setSearchQuery} />
      <DataGallery
        {...gallery}
        cardConfig={cardConfig}
        onCardClick={record => openProduct(record.id)}
        actions={[{ key: 'archive', label: 'Archive', onAction: rows => archive(rows) }]} />
    </View>
  );
}

Interaction model

  • Tap a card → onCardClick(record, rowIndex).
  • Long-press a card → toggle its selection (when row selection is enabled). While cards are selected a bottom selection bar shows the count, Select all, your actions and a clear button (web: floating ActionBar).
  • Pull to refresh → onRefresh; reaching the end → onEndReached (infinite paging).
  • The column count follows displayConfig.columnCount, clamped so each card is at least minCardWidth wide — one column on phones, more on tablets.

API Reference

useDataGallery

useDataGallery<TData>(options: UseDataGalleryProps<TData>) accepts every TanStack TableOptions<TData> (except pageCount / getCoreRowModel) plus:

OptionTypeDefaultDescription
dataTData[]—Rows (required).
columnsColumnDef<TData>[]—Columns (required). meta.cell drives value rendering.
displayConfigPartial<DataGalleryDisplayConfig>DEFAULT_DATA_GALLERY_DISPLAY_CONFIGInitial display config (re-hydrated when the prop identity changes).
onDisplayConfigChange(config: DataGalleryDisplayConfig) => void—Fires on every display change.
enableGroupingbooleanfalseGrouping pipeline.
enableSearchbooleanfalseClient-side global filter from the search input.
onSearch(query: string) => void—Debounced (200 ms) search callback (server keyword filtering).
minCardWidthnumber260Card width floor for the column clamp.
pagingMode'virtual-scroll' | 'standard''virtual-scroll''standard' adds the paging footer.
pageSizenumber24Initial page size (standard paging).
initialStateInitialTableState—Initial sorting / filters / visibility / order / pinning / grouping / selection / global filter.
metaTableMeta<TData>—Table meta (formatters, …).
dir'ltr' | 'rtl''ltr'Accepted for parity; ignored (RN uses I18nManager).
estimatedCardHeight / overscannumber—Accepted for parity; ignored (FlashList measures cards).

Return value (UseDataGalleryReturn<TData>):

FieldTypeDescription
table / tableMetaTable<TData> / TableMeta<TData>TanStack table + meta.
displayConfig / setDisplayConfigDataGalleryDisplayConfig / (updater) => voidDisplay state.
enableSearch, searchQuery, setSearchQuery—Search state (setter debounces the filter).
onClearFilters / hasActiveFilters() => void / booleanReset column filters + search.
selectedRowCount / getSelectedRowsnumber / () => TData[]Selection.
columnsPerRow / containerWidth / onContainerLayoutnumber / number / (event) => voidNative. Measured layout (replaces web containerRef / virtualizer).
minCardWidth, dir, pagingMode—Echoed options.

DataGallery

PropTypeDefaultDescription
tableTable<TData>—From the hook (required).
tableMetaTableMeta<TData>—From the hook (required).
displayConfigDataGalleryDisplayConfig—From the hook (required).
onClearFilters() => void—Empty-state "reset them" action.
hasActiveFiltersbooleanfalseSwitches the empty-state copy.
selectedRowCount / getSelectedRows / columnsPerRow——Accepted from the hook spread (selection is read from the table state; columns are measured).
onContainerLayout(event: LayoutChangeEvent) => void—Native. Keeps the hook's columnsPerRow in sync.
minCardWidthnumber260Card width floor for the column clamp.
pagingMode'virtual-scroll' | 'standard''virtual-scroll''standard' renders DataGridPaginationFooter.
heightnumber | 'auto''auto'dp height, or fill the parent.
cardConfigDataGalleryCardConfig<TData>—Slot bindings + render hooks.
actionsDataGalleryAction<TData>[]—Selection-bar actions.
onCardClick(record: TData, rowIndex: number) => void—Card tap.
onAdd / addLabel() => void / string— / 'Add card'"+ Add card" action after the cards.
isReloadingbooleanfalseBackground refetch indicator.
startContent / endContentReactNode—Slots above / below the cards.
onRefresh() => void—Native. Pull-to-refresh.
refreshingbooleanisReloadingNative. Pull-to-refresh spinner.
onEndReached() => void—Native. Infinite paging.
emptyTextstring—Native. Empty-state title override.
dir'ltr' | 'rtl'—Accepted for parity.
classNamestring—Root classes.

DataGalleryAction<TData>: key?, label, icon?: ReactNode, variant?: 'default' | 'destructive', onAction?(rows), render?(rows).

DataGalleryToolbar

PropTypeDefaultDescription
tableTable<TData>—Required.
displayConfig / onDisplayConfigChangeconfig / updater—Required — drives the display menu.
cardConfig / onCardConfigChangeDataGalleryCardConfigSerializable / updater—Card field bindings (card-config menu).
searchQuery / onSearchQueryChangestring / (value) => void''Search input (shown when onSearchQueryChange is set).
enableSearchbooleantrueSearch input.
enableFilterbooleantrueDataGridFilterMenu.
enableSortbooleantrueDataGridSortMenu.
enableGroupbooleanfalseDataGridGroupMenu.
enableDisplaybooleantrueDataGalleryDisplayMenu.
enableCardConfigbooleanfalseDataGalleryCardConfigMenu.
enableViewbooleanfalseDataGridViewMenu (local saved views).
viewStorageKeystring—Saved-view namespace.
disabledbooleanfalseDisable every control.
startContent / endContentReactNode—Slots.
onAdd / addLabel() => void / string— / 'Add'Add button.
classNamestring—Root classes.

DataGallerySearch

PropTypeDefaultDescription
valuestring—Current value.
onChange(value: string) => void—Change handler (debounce upstream).
placeholderstring'Search...'Placeholder.
disabledbooleanfalseDisable the input.
classNamestring—Classes.

DataGalleryCard

PropTypeDescription
row / table / tableMetaTanStackRow to render.
cardConfigDataGalleryCardConfig<TData>Slots + render hooks.
displayConfigDataGalleryDisplayConfigDisplay config.
enableSelectionbooleanLong-press toggles selection + checkbox.
onCardClick(record, rowIndex) => voidCard tap.
classNamestringNative. Card classes.

DataGalleryDisplayConfig

FieldTypeDefault
columnCount'flex' | 1 | 2 | 3 | 4 | 5 | 6'flex'
cardSize'full' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl''full'
density'compact' | 'comfortable''comfortable'
coverStyle'none' | 'top-sm' | 'top-md' | 'top-lg' | 'left''top-md'
layoutOrientation'vertical' | 'horizontal''vertical'
showFieldLabels / showCardHeader / showCardFooterbooleantrue / true / false
cardVariant'detailed' | 'compact' | 'media' | 'profile' | 'product''detailed'

DataGalleryCardConfig

Serializable slots (DataGalleryCardConfigSerializable): titleField, descriptionField, subtitleField, avatarField, coverImageField, badgeField, timelineField (column ids), bodyFields, footerFields (column id arrays), variant. Render hooks: renderCard({ record, rowIndex, isSelected, onToggleSelected }), renderCover({ record, value }), renderAvatar({ record, value }), renderBadge({ record, value }), renderTimeline({ record, value }), renderActions({ record, rowIndex }).

Components

ComponentDescription
DataGalleryFlashList card gallery.
DataGalleryToolbarSearch + scrollable menu chips.
DataGallerySearchSearch input.
DataGalleryCardCard engine (shared with the grid's gallery mode).
DataGalleryCardFieldOne labelled field value inside a card.
DataGalleryDisplayMenuDisplay sheet (variant, columns, size, density, cover, toggles).
DataGalleryCardConfigMenuCard slot bindings sheet.
DataGallerySkeleton / DataGallerySkeletonGrid / DataGallerySkeletonToolbarLoading placeholders.

Exports

ExportKind
useDataGalleryHook
resolveColumnsPerRow, resolveImageUrl, resolveCardLayout, resolveBodyFields, getCardSizePx, getCoverHeightPx, getCellValue, getRawCellValue, getCellOpts, getColumnLabel, detectCardConfigFromFieldsHelpers
SYSTEM_AUDIT_SLUGS, VARIANT_DISPLAY_PRESETS, VARIANT_VISUAL_DEFAULTS, DEFAULT_GAP, DEFAULT_MIN_CARD_WIDTHConstants
DATA_GALLERY_DEFAULT_PAGE_SIZE, DATA_GALLERY_PAGE_SIZE_OPTIONS, DEFAULT_DATA_GALLERY_DISPLAY_CONFIG, isSavedDataGalleryViewConstants / guards

Type Exports

TypeDescription
UseDataGalleryProps<TData> / UseDataGalleryReturn<TData>Hook options / return.
DataGalleryProps<TData>, DataGalleryToolbarProps<TData>, DataGallerySearchProps, DataGalleryCardProps<TData>, DataGalleryCardFieldProps, DataGalleryDisplayMenuProps, DataGalleryCardConfigMenuProps<TData>, DataGallerySkeleton*PropsComponent props.
DataGalleryAction<TData>Selection-bar action.
DataGalleryCardConfig<TData>, DataGalleryCardConfigSerializableCard configuration.
DataGalleryDisplayConfig, DataGalleryCardVariant, DataGalleryColumnCount, DataGalleryCardSize, DataGalleryCoverStyle, DataGalleryLayoutOrientation, DataGalleryDensity, DataGalleryPagingModeDisplay unions.
SavedDataGalleryViewSaved view shape.

Native deltas

  • FlashList with numColumns replaces @tanstack/react-virtual; the width is measured with onLayout (web: ResizeObserver). containerRef / virtualizer are gone; dir, estimatedCardHeight, overscan are accepted and ignored.
  • Long-press selects (no hover checkbox); the selection bar is the grid's bottom bar.
  • Toolbar = search on top + one horizontally scrollable row of chips opening bottom sheets.
  • Additions: onRefresh / refreshing, onEndReached, emptyText, onContainerLayout.

Translation keys

ui.dataGallery.addCard, add, searchPlaceholder, emptyTitle, emptyDescription, emptyFilteredTitle, emptyFilteredHint, clearFilters (web keys), plus the grid's ui.dataGrid.selectAll / clearSelection for the selection bar.

On this page