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.
Installation
pnpm dlx @docyrus/cli add @docyrus/rn-data-gallerypnpm add @tanstack/react-table @shopify/flash-list tailwind-variantsDataGallery 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
actionsand a clear button (web: floatingActionBar). - Pull to refresh →
onRefresh; reaching the end →onEndReached(infinite paging). - The column count follows
displayConfig.columnCount, clamped so each card is at leastminCardWidthwide — one column on phones, more on tablets.
API Reference
useDataGallery
useDataGallery<TData>(options: UseDataGalleryProps<TData>) accepts every TanStack TableOptions<TData> (except pageCount / getCoreRowModel) plus:
| Option | Type | Default | Description |
|---|---|---|---|
data | TData[] | — | Rows (required). |
columns | ColumnDef<TData>[] | — | Columns (required). meta.cell drives value rendering. |
displayConfig | Partial<DataGalleryDisplayConfig> | DEFAULT_DATA_GALLERY_DISPLAY_CONFIG | Initial display config (re-hydrated when the prop identity changes). |
onDisplayConfigChange | (config: DataGalleryDisplayConfig) => void | — | Fires on every display change. |
enableGrouping | boolean | false | Grouping pipeline. |
enableSearch | boolean | false | Client-side global filter from the search input. |
onSearch | (query: string) => void | — | Debounced (200 ms) search callback (server keyword filtering). |
minCardWidth | number | 260 | Card width floor for the column clamp. |
pagingMode | 'virtual-scroll' | 'standard' | 'virtual-scroll' | 'standard' adds the paging footer. |
pageSize | number | 24 | Initial page size (standard paging). |
initialState | InitialTableState | — | Initial sorting / filters / visibility / order / pinning / grouping / selection / global filter. |
meta | TableMeta<TData> | — | Table meta (formatters, …). |
dir | 'ltr' | 'rtl' | 'ltr' | Accepted for parity; ignored (RN uses I18nManager). |
estimatedCardHeight / overscan | number | — | Accepted for parity; ignored (FlashList measures cards). |
Return value (UseDataGalleryReturn<TData>):
| Field | Type | Description |
|---|---|---|
table / tableMeta | Table<TData> / TableMeta<TData> | TanStack table + meta. |
displayConfig / setDisplayConfig | DataGalleryDisplayConfig / (updater) => void | Display state. |
enableSearch, searchQuery, setSearchQuery | — | Search state (setter debounces the filter). |
onClearFilters / hasActiveFilters | () => void / boolean | Reset column filters + search. |
selectedRowCount / getSelectedRows | number / () => TData[] | Selection. |
columnsPerRow / containerWidth / onContainerLayout | number / number / (event) => void | Native. Measured layout (replaces web containerRef / virtualizer). |
minCardWidth, dir, pagingMode | — | Echoed options. |
DataGallery
| Prop | Type | Default | Description |
|---|---|---|---|
table | Table<TData> | — | From the hook (required). |
tableMeta | TableMeta<TData> | — | From the hook (required). |
displayConfig | DataGalleryDisplayConfig | — | From the hook (required). |
onClearFilters | () => void | — | Empty-state "reset them" action. |
hasActiveFilters | boolean | false | Switches 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. |
minCardWidth | number | 260 | Card width floor for the column clamp. |
pagingMode | 'virtual-scroll' | 'standard' | 'virtual-scroll' | 'standard' renders DataGridPaginationFooter. |
height | number | 'auto' | 'auto' | dp height, or fill the parent. |
cardConfig | DataGalleryCardConfig<TData> | — | Slot bindings + render hooks. |
actions | DataGalleryAction<TData>[] | — | Selection-bar actions. |
onCardClick | (record: TData, rowIndex: number) => void | — | Card tap. |
onAdd / addLabel | () => void / string | — / 'Add card' | "+ Add card" action after the cards. |
isReloading | boolean | false | Background refetch indicator. |
startContent / endContent | ReactNode | — | Slots above / below the cards. |
onRefresh | () => void | — | Native. Pull-to-refresh. |
refreshing | boolean | isReloading | Native. Pull-to-refresh spinner. |
onEndReached | () => void | — | Native. Infinite paging. |
emptyText | string | — | Native. Empty-state title override. |
dir | 'ltr' | 'rtl' | — | Accepted for parity. |
className | string | — | Root classes. |
DataGalleryAction<TData>: key?, label, icon?: ReactNode, variant?: 'default' | 'destructive', onAction?(rows), render?(rows).
DataGalleryToolbar
| Prop | Type | Default | Description |
|---|---|---|---|
table | Table<TData> | — | Required. |
displayConfig / onDisplayConfigChange | config / updater | — | Required — drives the display menu. |
cardConfig / onCardConfigChange | DataGalleryCardConfigSerializable / updater | — | Card field bindings (card-config menu). |
searchQuery / onSearchQueryChange | string / (value) => void | '' | Search input (shown when onSearchQueryChange is set). |
enableSearch | boolean | true | Search input. |
enableFilter | boolean | true | DataGridFilterMenu. |
enableSort | boolean | true | DataGridSortMenu. |
enableGroup | boolean | false | DataGridGroupMenu. |
enableDisplay | boolean | true | DataGalleryDisplayMenu. |
enableCardConfig | boolean | false | DataGalleryCardConfigMenu. |
enableView | boolean | false | DataGridViewMenu (local saved views). |
viewStorageKey | string | — | Saved-view namespace. |
disabled | boolean | false | Disable every control. |
startContent / endContent | ReactNode | — | Slots. |
onAdd / addLabel | () => void / string | — / 'Add' | Add button. |
className | string | — | Root classes. |
DataGallerySearch
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Current value. |
onChange | (value: string) => void | — | Change handler (debounce upstream). |
placeholder | string | 'Search...' | Placeholder. |
disabled | boolean | false | Disable the input. |
className | string | — | Classes. |
DataGalleryCard
| Prop | Type | Description |
|---|---|---|
row / table / tableMeta | TanStack | Row to render. |
cardConfig | DataGalleryCardConfig<TData> | Slots + render hooks. |
displayConfig | DataGalleryDisplayConfig | Display config. |
enableSelection | boolean | Long-press toggles selection + checkbox. |
onCardClick | (record, rowIndex) => void | Card tap. |
className | string | Native. Card classes. |
DataGalleryDisplayConfig
| Field | Type | Default |
|---|---|---|
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 / showCardFooter | boolean | true / 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
| Component | Description |
|---|---|
DataGallery | FlashList card gallery. |
DataGalleryToolbar | Search + scrollable menu chips. |
DataGallerySearch | Search input. |
DataGalleryCard | Card engine (shared with the grid's gallery mode). |
DataGalleryCardField | One labelled field value inside a card. |
DataGalleryDisplayMenu | Display sheet (variant, columns, size, density, cover, toggles). |
DataGalleryCardConfigMenu | Card slot bindings sheet. |
DataGallerySkeleton / DataGallerySkeletonGrid / DataGallerySkeletonToolbar | Loading placeholders. |
Exports
| Export | Kind |
|---|---|
useDataGallery | Hook |
resolveColumnsPerRow, resolveImageUrl, resolveCardLayout, resolveBodyFields, getCardSizePx, getCoverHeightPx, getCellValue, getRawCellValue, getCellOpts, getColumnLabel, detectCardConfigFromFields | Helpers |
SYSTEM_AUDIT_SLUGS, VARIANT_DISPLAY_PRESETS, VARIANT_VISUAL_DEFAULTS, DEFAULT_GAP, DEFAULT_MIN_CARD_WIDTH | Constants |
DATA_GALLERY_DEFAULT_PAGE_SIZE, DATA_GALLERY_PAGE_SIZE_OPTIONS, DEFAULT_DATA_GALLERY_DISPLAY_CONFIG, isSavedDataGalleryView | Constants / guards |
Type Exports
| Type | Description |
|---|---|
UseDataGalleryProps<TData> / UseDataGalleryReturn<TData> | Hook options / return. |
DataGalleryProps<TData>, DataGalleryToolbarProps<TData>, DataGallerySearchProps, DataGalleryCardProps<TData>, DataGalleryCardFieldProps, DataGalleryDisplayMenuProps, DataGalleryCardConfigMenuProps<TData>, DataGallerySkeleton*Props | Component props. |
DataGalleryAction<TData> | Selection-bar action. |
DataGalleryCardConfig<TData>, DataGalleryCardConfigSerializable | Card configuration. |
DataGalleryDisplayConfig, DataGalleryCardVariant, DataGalleryColumnCount, DataGalleryCardSize, DataGalleryCoverStyle, DataGalleryLayoutOrientation, DataGalleryDensity, DataGalleryPagingMode | Display unions. |
SavedDataGalleryView | Saved view shape. |
Native deltas
- FlashList with
numColumnsreplaces@tanstack/react-virtual; the width is measured withonLayout(web:ResizeObserver).containerRef/virtualizerare gone;dir,estimatedCardHeight,overscanare 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.
CreateRecordDialog
Bottom-sheet dialog for quick record creation — selectors with dependent clearing, @ /
DataGrid
Headless spreadsheet grid for React Native — useDataGrid + DataGrid with FlashList virtualization, pinned columns, every cell variant, sheet editors, status transitions, grouping, change tracking, paging, color rules and gallery mode.