@datafluxgrid/fluxgrid-data

The query layer for
frontend arrays

Define your search, sort, filter, and pagination rules once. Run them anywhere. Get back everything your UI needs in one call.

$ npm install @datafluxgrid/fluxgrid-data
~1 kB
Minzipped
0
Dependencies
17
Result fields
~5
Lines to setup

Get started in seconds

Zero dependencies. Works with any JavaScript environment — no adapter, no build config required.

npm install @datafluxgrid/fluxgrid-data
HTML
<script src="https://cdn.jsdelivr.net/npm/@datafluxgrid/fluxgrid-data/dist/index.js"></script>
<script>
  const q = createQuery({ search: ['name'], pageSize: 10 });
</script>
JavaScript TypeScript React Vue Svelte Angular Next.js Nuxt Plain HTML

Without fluxgrid-data

JS — written in every component
const filtered = users
  .filter(u => ['name','email']
    .some(f => u[f].toLowerCase()
      .includes(term)))
  .sort((a,b) =>
    a.name.localeCompare(b.name));

const total   = filtered.length;
const pages   = Math.ceil(total/10);
const data    = filtered.slice(
  (page-1)*10, page*10);
const hasNext = page < pages;
const hasPrev = page > 1;
const from    = (page-1)*10+1;
const to      = Math.min(page*10,total);
// isEmpty, isNoResults, allSelected,
// someSelected, ms — still missing

With fluxgrid-data

JS — defined once, used everywhere
import { createQuery } from
  '@datafluxgrid/fluxgrid-data';

const userQuery = createQuery({
  search:   ['name', 'email'],
  sort:     { field: 'name' },
  pageSize: 10,
});

const result = userQuery.run(
  users, { term, page }
);
// result has everything ↓
JS — console.log(result)
{
  data: [
    { id: 1,  name: "Arjun Sharma",  role: "Admin",  salary: 95000  },
    { id: 2,  name: "Priya Nair",    role: "Editor", salary: 72000  },
    { id: 3,  name: "Rahul Mehta",   role: "Viewer", salary: 61000  },
    { id: 4,  name: "Sneha Patel",   role: "Editor", salary: 78000  },
    { id: 5,  name: "Vikram Singh",  role: "Admin",  salary: 102000 },
    { id: 6,  name: "Divya Reddy",   role: "Viewer", salary: 58000  },
    { id: 7,  name: "Karthik Iyer",  role: "Editor", salary: 85000  },
    { id: 8,  name: "Meera Nair",    role: "Viewer", salary: 55000  },
    { id: 9,  name: "Aditya Kumar",  role: "Admin",  salary: 110000 },
    { id: 10, name: "Lakshmi Rao",   role: "Editor", salary: 74000  },
  ],

  total:          47,    // total matching records across all pages
  page:            1,    // current page number
  pages:           5,    // total pages  (47 records / 10 per page)
  pageSize:       10,    // rows per page
  from:            1,    // first record on this page
  to:             10,    // last record — use for "Showing 1–10 of 47"

  hasNext:      true,    // page 1 of 5 — next page exists
  hasPrev:     false,    // page 1 — no previous page

  term:          "a",    // active search term — use to highlight matched text
  sortField:  "name",    // active sort column — show arrow indicator on header
  sortDir:     "asc",    // sort direction — A → Z

  isEmpty:     false,    // false — data exists, no empty state needed
  isNoResults: false,    // false — search returned results

  allSelected:  false,   // not all 10 rows on this page are checked
  someSelected:  true,   // rows 1 and 3 checked — show indeterminate checkbox

  ms:              1,    // entire query ran in 1ms
}

Real use cases

The same createQuery API handles every common frontend data pattern.

React — UsersTable.jsx
import { useState } from 'react';
import { createQuery } from '@datafluxgrid/fluxgrid-data';

const userQuery = createQuery({
  search:   ['name', 'email', 'department'],
  sort:     { field: 'name', dir: 'asc' },
  pageSize: 10,
});

export function UsersTable({ users }) {
  const [term, setTerm]         = useState('');
  const [page, setPage]         = useState(1);
  const [sortField, setSortField] = useState('name');
  const [sortDir, setSortDir]   = useState('asc');
  const [roleFilter, setRole]   = useState('');
  const [selected, setSelected] = useState([]);

  const result = userQuery.run(users, {
    term, page, sortField, sortDir, selected,
    filterFn: u => !roleFilter || u.role === roleFilter,
  });

  return (
    <>
      <input value={term}
        onChange={e => { setTerm(e.target.value); setPage(1); }} />

      {result.isEmpty     && <p>No users yet.</p>}
      {result.isNoResults && <p>No results for "{result.term}"</p>}

      <p>Showing {result.from}–{result.to} of {result.total}</p>

      {result.data.map(user => (
        <tr key={user.id}><td>{user.name}</td></tr>
      ))}

      <button disabled={!result.hasPrev}
        onClick={() => setPage(p => p - 1)}>Prev</button>
      <button disabled={!result.hasNext}
        onClick={() => setPage(p => p + 1)}>Next</button>
    </>
  );
}
TypeScript — full type safety
import { createQuery } from '@datafluxgrid/fluxgrid-data';

interface User {
  id:         number;
  name:       string;
  role:       'Admin' | 'Editor' | 'Viewer';
  department: string;
  salary:     number;
}

const userQuery = createQuery<User>({
  search:   ['name', 'department'],
  sort:     { field: 'salary', dir: 'desc' },
  pageSize: 5,
});

const result = userQuery.run(users, { term, page });
// result.data    → typed as User[]
// result.total   → typed as number
// result.hasNext → typed as boolean
GroupBy — Kanban / Analytics
const taskQuery = createQuery({
  search:  ['title', 'assignee'],
  groupBy: 'status',
  sort:    { field: 'priority', dir: 'asc' },
});

const { groups, total } = taskQuery.run(tasks, { term });

// groups = {
//   'Todo':        [{ title: 'Design page', ... }],
//   'In Progress': [{ title: 'Fix bug', ... }],
//   'Done':        [{ title: 'Write docs', ... }],
// }

Object.entries(groups).map(([status, items]) => (
  <section key={status}>
    <h3>{status} ({items.length})</h3>
    {items.map(task => <TaskCard task={task} />)}
  </section>
))
Serializable pipelines — save & restore
const saved = userQuery.toJSON();
localStorage.setItem('myQuery', JSON.stringify(saved));

const restored = createQuery.fromJSON(
  JSON.parse(localStorage.getItem('myQuery'))
);

// toJSON() produces:
// {
//   search:   ['name', 'email'],
//   sort:     { field: 'name', dir: 'asc' },
//   pageSize: 10
// }

// Use cases:
// — Save user filter view between sessions
// — Share filtered table via URL param
// — Send pipeline to server for execution
Vue 3 — computed query
<script setup>
import { ref, computed } from 'vue';
import { createQuery } from '@datafluxgrid/fluxgrid-data';

const props = defineProps(['users']);
const term  = ref('');
const page  = ref(1);

const userQuery = createQuery({
  search: ['name', 'role'],
  sort:   { field: 'name' },
  pageSize: 10,
});

const result = computed(() =>
  userQuery.run(props.users, {
    term: term.value, page: page.value
  })
);
</script>

<template>
  <input v-model="term" @input="page = 1" />
  <p>{{ result.from }}–{{ result.to }} of {{ result.total }}</p>
  <button :disabled="!result.hasPrev" @click="page--">Prev</button>
  <button :disabled="!result.hasNext" @click="page++">Next</button>
</template>

Complete API

Two functions. One config object. One options object.

createQuery(config)

Option Type Description
search string[] Fields to search across when term is provided
sort { field, dir } Default sort field and direction. dir is 'asc' or 'desc'
pageSize number Records per page. Default is 10
filter (item) => boolean Static filter applied on every .run() call
groupBy string Group records by this field. Changes result shape to { groups, total, ms }

query.run(data, options)

Option Type Description
term string Search term. Empty string means no filter — show all
page number Current page number. Starts at 1
pageSize number Override pageSize from config for this run
sortField string Override sort field from config for this run
sortDir 'asc' | 'desc' Override sort direction for this run
filterFn (item) => boolean Dynamic filter — for dropdowns, sliders, toggles
selected (string | number)[] Selected row IDs for allSelected / someSelected helpers

Result object

Field Type Description
data T[] Records for the current page — render this
total number Total matching records across all pages
page number Current page number
pages number Total number of pages
pageSize number Records per page
from number First record number — for "Showing 21–30 of 247"
to number Last record number
hasNext boolean True if there is a next page
hasPrev boolean True if there is a previous page
term string Active search term — use for highlighting matched text
sortField string Active sort field — show sort arrow on column header
sortDir string Active sort direction — 'asc' or 'desc'
isEmpty boolean No data and no term — show empty state illustration
isNoResults boolean Search returned nothing — show "no results" message
allSelected boolean All rows on current page are selected
someSelected boolean Some rows selected — for indeterminate checkbox state
ms number Query execution time in milliseconds

Competitor comparison

Stop copy-pasting filter logic. One package covers search, sort, paginate, group, and gives you full UI meta in every result.

Feature fluxgrid-data @tanstack/react-table fuse.js match-sorter datapipe-js
Search across fields Yes Yes Yes Yes Yes
Sort Yes Yes No Yes Yes
Paginate Yes Yes No No No
GroupBy Yes Yes No No Yes
Full meta output (total, from, to, hasNext…) Yes No — manual No No No
isEmpty / isNoResults states Yes No No No No
Checkbox helpers (allSelected / someSelected) Yes No No No No
Serializable toJSON() / fromJSON() Yes No No No No
Framework agnostic Yes No — adapter Yes Yes Yes
No columns config required Yes No — mandatory Yes Yes Yes
Zero dependencies Yes Yes Yes No No
TypeScript support Yes Yes Yes Yes Partial
CDN script tag Yes No Yes No No
Actively maintained Yes Yes Yes Yes No
Bundle size (minzipped) ~1 kB ~15–20 kB ~5 kB ~3 kB ~8 kB
Setup lines of code ~5 ~40–60 ~10 ~5 ~10

Everything in one call

No plugin system. No config objects inside config objects. Every feature works out of the box.

Multi-field search

Search across any fields. Case insensitive. Partial match. Active term returned for text highlighting.

Sort any field

String and number sorting handled automatically. Override field and direction per run.

Smart pagination

from, to, hasNext, hasPrev, pages all returned automatically. Out-of-bounds pages clamped safely.

GroupBy

Group records by any field. Result changes to groups object. Perfect for Kanban and analytics.

Serializable pipelines

toJSON() and fromJSON() let you save, share, and restore query definitions. No other utility does this.

Selection helpers

Pass selected IDs and get allSelected and someSelected back. Indeterminate checkbox handled for you.