expo-flash-datagrid
v0.1.0
Published
Expo-first React Native data grid and data table built on FlashList with sorting, filtering, search, pagination, selection, and row actions.
Downloads
105
Maintainers
Readme
expo-flash-datagrid
React Native data grid and Expo data table built on top of FlashList, with virtualization, sorting, filtering, global search, pagination, row selection, row actions, and localization.
expo-flash-datagrid is designed for apps that need a more capable table than FlatList, while still keeping React Native ergonomics and strong TypeScript support.
If you are looking for a React Native data table, Expo data grid, mobile datagrid, or a FlashList-based grid component, this package is built for that use case.
What It Is
This package focuses on mobile-first tabular data experiences for Expo and React Native apps:
- CRM and admin lists
- inventory and catalog screens
- financial and operational tables
- searchable and filterable mobile datasets
- server-driven tables with pagination or infinite loading
Current Status
Expo-only for now.
The package currently depends on @expo/vector-icons and expo-font, so it is intended for Expo projects at the moment. A bare React Native version would require replacing that dependency chain first.
Features
- FlashList-powered rendering for large datasets
- Client and server data modes
- Sorting, filtering, global search, pagination, and infinite loading
- Column visibility management
- Selection model with mobile-oriented long-press behavior
- Row action menus and actions columns
- Theme overrides and locale overrides
- Built-in English and Portuguese locale bundles
- Exported pure utilities for sorting, filtering, search, and pagination
Why This Package
- Better fit than a plain
FlatListwhen you need real table behavior on mobile - Strong TypeScript API for rows, columns, filters, pagination, and events
- Familiar data-grid concepts inspired by desktop grids, adapted to touch-first UX
- Good fit for Expo apps that need an MUI-like data grid experience in React Native
Installation
This package currently expects an Expo-compatible setup because it uses @expo/vector-icons internally and safe-area aware modal surfaces.
If you already have an Expo app, react, react-native, expo, and expo-font are usually already present.
npm
npm install expo-flash-datagrid @shopify/flash-list react-native-safe-area-context @expo/vector-icons expo expo-fontyarn
yarn add expo-flash-datagrid @shopify/flash-list react-native-safe-area-context @expo/vector-icons expo expo-fontRequired peers in the host app:
reactreact-nativeexpoexpo-font@expo/vector-icons@shopify/flash-listreact-native-safe-area-context
Wrap your app with SafeAreaProvider:
import { SafeAreaProvider } from 'react-native-safe-area-context';
export default function App() {
return (
<SafeAreaProvider>
<YourScreen />
</SafeAreaProvider>
);
}Quick Start
import { useState } from 'react';
import { View } from 'react-native';
import {
DataGrid,
type ColumnDef,
type QueryState,
} from 'expo-flash-datagrid';
type User = {
id: string;
name: string;
email: string;
age: number;
active: boolean;
};
const rows: User[] = [
{
id: '1',
name: 'Alice',
email: '[email protected]',
age: 29,
active: true,
},
{
id: '2',
name: 'Bruno',
email: '[email protected]',
age: 34,
active: false,
},
];
const columns: ColumnDef<User>[] = [
{ field: 'name', headerName: 'Name', flex: 1, searchable: true },
{ field: 'email', headerName: 'Email', flex: 1.2, searchable: true },
{ field: 'age', headerName: 'Age', type: 'number', width: 90 },
{ field: 'active', headerName: 'Active', type: 'boolean', width: 90 },
];
const initialState: QueryState = {
paginationModel: { page: 0, pageSize: 25 },
sortModel: [],
filterModel: { items: [], logicOperator: 'and' },
searchText: '',
columnVisibilityModel: {},
selectionModel: [],
};
export function UsersScreen() {
const [state, setState] = useState<QueryState>(initialState);
return (
<View style={{ flex: 1 }}>
<DataGrid<User>
mode="client"
rows={rows}
columns={columns}
state={state}
onStateChange={setState}
checkboxSelection
zebraRows
pageSizeOptions={[10, 25, 50]}
style={{ flex: 1 }}
/>
</View>
);
}Data Modes
Client mode
In client mode, the grid applies these steps locally on rows:
- Filter
- Search
- Sort
- Paginate
Use this mode when the full dataset is already available in memory.
<DataGrid<User>
mode="client"
rows={rows}
columns={columns}
state={state}
onStateChange={setState}
enableMultiSort
searchDebounceMs={300}
pageSizeOptions={[10, 25, 50]}
/>Server mode
In server mode, the grid does not transform rows locally. It emits state changes and you fetch the correct slice from your API.
import { useEffect, useState } from 'react';
import {
DataGrid,
type QueryState,
} from 'expo-flash-datagrid';
const initialState: QueryState = {
paginationModel: { page: 0, pageSize: 25 },
sortModel: [],
filterModel: { items: [], logicOperator: 'and' },
searchText: '',
columnVisibilityModel: {},
selectionModel: [],
};
export function ServerUsersScreen() {
const [state, setState] = useState<QueryState>(initialState);
const [rows, setRows] = useState<User[]>([]);
const [rowCount, setRowCount] = useState(0);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
// Call your API using:
// state.paginationModel
// state.sortModel
// state.filterModel
// state.searchText
//
// Then update rows and rowCount.
setLoading(false);
}, [state]);
return (
<DataGrid<User>
mode="server"
rows={rows}
columns={columns}
rowCount={rowCount}
state={state}
onStateChange={setState}
loading={loading}
/>
);
}Common Customizations
Empty state and localization
The grid ships with en and pt locale bundles. You can override the empty-state text directly with emptyLabel or provide a partial localeText.
<DataGrid<User>
mode="client"
rows={rows}
columns={columns}
locale="pt"
emptyLabel="Nenhum cliente encontrado"
localeText={{
rowsPerPage: 'Itens por pagina',
}}
/>Exported helpers:
EN_LOCALE_TEXTPT_LOCALE_TEXTresolveLocaleTextformatLocale
Theme overrides
<DataGrid<User>
mode="client"
rows={rows}
columns={columns}
theme={{
headerBackground: '#E0F2FE',
toolbarBackground: '#F0F9FF',
footerBackground: '#F0F9FF',
selectionBackground: '#DBEAFE',
rowAltBackground: '#F8FAFC',
borderRadius: 16,
}}
/>Row actions
rowActions opens a native-feeling action menu, by default on long press.
import { MaterialIcons } from '@expo/vector-icons';
const rowActions = [
{
key: 'edit',
label: 'Edit',
icon: ({ color, size }: { color: string; size: number }) => (
<MaterialIcons name="edit" color={color} size={size} />
),
onPress: ({ rowId }: { rowId: string }) => {
console.log('edit', rowId);
},
},
];
<DataGrid<User>
mode="client"
rows={rows}
columns={columns}
rowActions={rowActions}
/>Actions column
You can also add a dedicated column with type: 'actions' and provide per-row actions.
import { MaterialIcons } from '@expo/vector-icons';
const columns: ColumnDef<User>[] = [
{ field: 'name', headerName: 'Name', flex: 1 },
{
field: 'actions',
headerName: 'Actions',
type: 'actions',
width: 132,
actions: ({ rowId }) => [
{
key: `edit-${rowId}`,
label: 'Edit',
icon: ({ color, size }) => (
<MaterialIcons name="edit" color={color} size={size} />
),
onPress: () => {
console.log(rowId);
},
},
],
actionTrigger: {
display: 'both',
label: 'Actions',
icon: ({ color, size }) => (
<MaterialIcons name="more-horiz" color={color} size={size} />
),
},
},
];Column Definition
ColumnDef<TRow> supports:
field,headerNamewidth,minWidth,flextype:'string' | 'number' | 'date' | 'datetime' | 'boolean' | 'actions' | 'custom'sortable,filterable,hideable,searchablevalueGetter,valueFormatterrenderCell,renderHeadersortComparator,filterOperatorsalign,headerAlignactions,actionTrigger
API Overview
Main DataGrid prop groups:
- Data:
rows,columns,getRowId - State:
state,initialState,onStateChange,onQueryChange - Sorting:
sortModel,onSortModelChange,enableMultiSort - Filtering:
filterModel,onFilterModelChange,maxFilters - Search:
searchText,onSearchTextChange,searchDebounceMs - Visibility:
columnVisibilityModel,onColumnVisibilityModelChange - Pagination:
paginationModel,onPaginationModelChange,pageSizeOptions,rowCount - Loading:
loading,refreshing,onRefresh,infinite,onEndReached - Selection:
selectionModel,onSelectionModelChange,checkboxSelection - Actions:
rowActions,rowActionMenuTrigger - Events:
onRowPress,onRowLongPress,onCellPress,onCellLongPress - UI:
theme,locale,localeText,emptyLabel,emptyState,loadingOverlay
Useful rendering props:
rowHeightestimatedItemSizelistModegetRowStylezebraRowscompacttoolbarTitle
Exported Utilities
The package also exports pure utilities that can be reused in adapters, tests, or custom hooks:
applySortingapplyFiltergetDefaultFilterOperatorsByTypeapplySearchpaginateRowsgetPaginationRangeapplyClientPipeline
Example App
The repository includes an Expo example in example/ with:
- 10k client rows
- simulated server mode
- infinite loading
- filters for multiple column types
- row actions and actions columns
- locale switching
- theme overrides
Run it with:
cd example
npx expo startLicense
MIT
