@designdaddy/datagrid
v0.1.1
Published
Design Daddy data grid — search, filters, freeze, sort, pagination
Maintainers
Readme
@designdaddy/datagrid
Self-contained React data grid with search, column/value filters, freeze, show/hide columns, header sort, and pagination.
Styles are built in — import Grid only. No theme package and no separate CSS import required. The grid wraps itself in .gds-root with its own tokens so host-page CSS will not break alignment.
Table of contents
- Install
- Quick start
- What to use (Grid vs pieces)
- Data modes
- Column shape
- Grid props by feature
- Feature toggles
- Icon vs text toolbar
- Search field
- Responsive behavior
- Styling
- Custom cells & actions
- Pagination
- Remote / API data
- App wrapper tips
- Exports
- Local pack / publish
- Run the docs UI
Install
npm install @designdaddy/datagridPeer dependency: React 17+ (18 / 19 supported).
In this monorepo:
cd designdaddy
npm installQuick start
1. Two parameters — columns + rows
import { Grid } from "@designdaddy/datagrid";
const columns = [
{ id: "name", label: "Name", width: 180 },
{ id: "email", label: "Email", width: 220 },
{ id: "role", label: "Role", width: 120 },
{ id: "status", label: "Status", width: 120 },
];
const rows = [
{ id: 1, name: "Aarav", email: "[email protected]", role: "Admin", status: "Active" },
{ id: 2, name: "Meera", email: "[email protected]", role: "Staff", status: "Pending" },
];
export default function UsersPage() {
return <Grid columns={columns} rows={rows} />;
}Search, sort, freeze, view, filters, clear, and pagination are included.
No extra CSS import — styles load with Grid.
2. One parameter — data (auto columns)
import { Grid } from "@designdaddy/datagrid";
const data = [
{ name: "Aarav", role: "Admin", status: "Active" },
{ name: "Meera", role: "Staff", status: "Pending" },
];
export default function UsersPage() {
return <Grid data={data} />;
}Object keys become columns; no columns array needed.
3. One parameter — URL (fetch + auto columns)
import { Grid } from "@designdaddy/datagrid";
export default function UsersPage() {
return <Grid data="/api/users" />;
}4. Toolbar — icons and toggles
<Grid columns={columns} rows={rows} toolbarIcons />
<Grid
columns={columns}
rows={rows}
showFreeze={false}
showPagination={false}
showToolbar={false}
/>Optional CSS fallback:
import "@designdaddy/datagrid/styles.css";What to use (Grid vs pieces)
| Piece | What it is | When to use | |-------|------------|-------------| | Grid | Finished product (toolbar + table + pagination, all wired) | Almost always | | GridShell | Empty layout with 3 slots: filters / grid / pagination | Custom composition | | GridToolbar | Filters strip only | Custom shell, toolbar-only UI | | GridSection | Finished table (headers, sort, empty state, rows) | Custom shell, table-only UI | | ColumnPicker | Freeze / View / Filter popover button | Custom toolbar | | Pagination | Page bar only | Custom shell | | Utils | Pure helpers (no UI) | Custom filter/sort/data logic |
Day to day: use Grid.
Use GridShell + pieces only when you need a custom layout.
TableCell exists for custom GridSection row markup. It is not a separate docs module.
Data modes
// Two params — classic
<Grid columns={columns} rows={rows} />
// One param — JSON (keys → columns)
<Grid data={[{ name: "Aarav", role: "Admin" }]} />
// One param — URL (fetch, then same)
<Grid data="/api/users" />
<Grid data="https://api.example.com/users" />
// Rows only (no columns) → auto columns from keys
<Grid rows={rows} />Supported data JSON shapes
| Shape | Result |
|-------|--------|
| [{ a: 1 }, { a: 2 }] | Rows as-is; columns from keys |
| { data \| rows \| results \| items: [...] } | Uses that array |
| { name: ["A","B"], age: [1,2] } | Columnar → row objects |
| Single object | One row |
<Grid
data="/api/users"
fetchOptions={{ headers: { Authorization: "Bearer …" } }}
/>Column shape
| Field | Type | Required | Description |
|--------|------|----------|-------------|
| id | string | yes | Field key on each row |
| label | string | yes* | Header text (*auto from key in one-param mode) |
| width | number | recommended | Pixel width |
| sortable | boolean | no | Default true |
| className | string | no | Extra class on <th> |
| cellClassName | string | no | Extra class on <td> |
Prefer a unique id on each row, or set rowKey.
Grid props by feature
Data
| Prop | Default | Description |
|------|---------|-------------|
| columns | auto | Column defs. Omit / empty → derive from keys |
| rows | [] | Row objects |
| data | — | One-param: JSON or URL |
| rowKey | "id" | Unique key field |
| fetchOptions | — | Extra fetch init for URL data |
Toolbar & filters
| Prop | Default | Description |
|------|---------|-------------|
| showToolbar | auto | Master switch for filters section |
| showFreeze | true | Freeze button |
| showView | true | Show/hide columns |
| showFilterColumns | true | Filter-columns picker |
| showColumnFilter | true | Column + Value dropdowns |
| showClear | true | Clear button |
| showSearch | true | Search field |
| toolbarIcons | false | Force icons at all widths |
| searchPlaceholder | "Search any column…" | Search placeholder |
Pagination
| Prop | Default | Description |
|------|---------|-------------|
| showPagination | true | Pagination bar |
| pageSize | 10 | Initial rows per page |
| pageSizeOptions | [5,10,15,20,25,50,75,100] | Rows presets |
| pageSizeMin / Max / Step | 5 / 100 / 5 | Custom size bounds |
| showPageSizeCustom | true | Custom input (hidden on narrow screens) |
Custom rendering
| Prop | Description |
|------|-------------|
| renderCell | (columnId, row, column) => ReactNode |
| renderActions | (row) => ReactNode — adds Actions column |
Styling
| Prop | Description |
|------|-------------|
| className | Extra class on .gds-root |
| style | Inline styles / CSS variables |
| theme | Design tokens → CSS variables |
Empty state & a11y
| Prop | Default |
|------|---------|
| emptyMessage | "No records found." |
| emptyHint | package hint |
| tableAriaLabel | "Data grid" |
Feature toggles
<Grid
columns={columns}
rows={rows}
showFreeze={false}
showView={false}
showFilterColumns={false}
showColumnFilter={false}
showClear={false}
showSearch={true}
showPagination={false}
showPageSizeCustom={false}
showToolbar={false}
/>| Prop | Hides |
|------|--------|
| showFreeze={false} | Freeze |
| showView={false} | View |
| showFilterColumns={false} | Filter-columns picker |
| showColumnFilter={false} | Column + Value |
| showClear={false} | Clear |
| showSearch={false} | Search |
| showToolbar={false} | Entire filters section |
| showPagination={false} | Pagination (shows all filtered rows) |
| showPageSizeCustom={false} | Custom size input |
Icon vs text toolbar
<Grid columns={columns} rows={rows} /> // text on desktop; icons auto ≤900px
<Grid columns={columns} rows={rows} toolbarIcons /> // icons alwaysButtons keep title / aria-label. Count badges (e.g. View 4) still show in icon mode.
Search field
- No separate “Search” text label.
- Magnifying-glass icon sits inside the input.
- Placeholder:
searchPlaceholder(default"Search any column…").
Responsive behavior
Layout follows grid width (container queries on .gds-root), not only the browser width.
| Width | Toolbar | Pagination |
|-------|---------|------------|
| Wide | Text buttons (unless toolbarIcons) | Prev/Next text, Rows + Custom, full info |
| ≤900px | Auto icons, compact | One row: chevrons · pages · Rows · short info |
| ≤520px | Tools + Column/Value; search may wrap | Still one pagination row |
Use parent minWidth: 0 and avoid overflow-x: hidden on ancestors.
Styling
Two approaches: your classes and built-in classes. Import your CSS after Grid so overrides win.
A. Your own className / theme / style
import { Grid } from "@designdaddy/datagrid";
import "./App.css"; // AFTER Grid
<Grid
className="my-grid"
theme={{
primary: "#0f766e",
border: "#e2e8f0",
text: "#0f172a",
muted: "#64748b",
radius: "10px",
fontSize: "15px",
}}
style={{ "--gds-text-md": "13px" }}
columns={columns}
rows={rows}
/>/* App.css */
.my-grid {
--ub-teal: #0f766e;
--ub-border: #dbe3ee;
}
.my-grid .ub-table th {
font-size: 11px;
}B. Built-in class names (default look)
Override these under your wrapper:
| Class | What it styles |
|-------|----------------|
| .gds-root | Root + CSS variables |
| .gds-shell | filters / grid / pagination layout |
| .gds-shell__filters / __grid / __pagination | Slots |
| .gds-toolbar | Filters toolbar card |
| .gds-toolbar__tools / __filters / __search | Toolbar regions |
| .gds-search-field / __icon | Search input + icon |
| .gds-column-picker / __panel / __count | Freeze/View/Filter popover |
| .gff-btn / --sm / --ghost | Buttons |
| .gff-input / .gff-select | Inputs and selects |
| .ub-table-wrap / .ub-table | Table |
| .ub-table th / td | Headers and cells |
| .user-builder-pagination | Pagination bar |
| .user-builder-pagination-page--active | Active page |
.my-grid .gds-toolbar { box-shadow: none; }
.my-grid .user-builder-pagination-page--active { background: #0f766e; }Limits (what you can / cannot do)
You can
- Change colors, fonts, borders, radius via CSS variables /
theme/ scoped CSS - Add
classNameonGridand style children (.my-grid .ub-table …) - Set
className/cellClassNameon column defs - Restyle toolbar, table, and pagination independently
You should not
- Remove core layout classes (
.gds-root,.gds-shell,.ub-table, …) — layout/freeze can break - Rely on global
button/inputstyles without scoping under.my-grid - Import your CSS before
Grid(package CSS may load later and override you)
Theme tokens
| Token / theme key | Controls |
|---------------------|----------|
| --ub-teal / primary | Accent |
| --ub-teal-dark / primaryDark | Dark accent |
| --ub-teal-soft / primarySoft | Soft accent |
| --ub-border / border | Borders |
| --ub-text / text | Main text |
| --ub-muted / muted | Muted labels |
| --ub-bg / background | Root background |
| --ub-surface / surface | Cards / inputs / table |
| --ub-header-bg / headerBg | Table header |
| --ub-radius / radius | Corner radius |
| --gds-root-font-size / fontSize | Root font size |
| --gds-font-family / fontFamily | Font stack |
| --gds-text-sm / textSm | Small labels |
| --gds-text-md / textMd | Inputs & buttons |
| --gds-text-row / textRow | Picker rows |
Raw vars also work: theme={{ "--ub-teal": "#c00" }} or style={{ "--ub-teal": "#c00" }}.
Each docs module page (Grid, GridToolbar, GridSection, …) has its own Styling section with the classes for that piece.
Custom cells & actions
<Grid
columns={columns}
rows={rows}
renderCell={(columnId, row) => {
if (columnId === "status") {
return (
<span className={`ub-status ub-status--${String(row.status).toLowerCase()}`}>
{row.status}
</span>
);
}
return row[columnId] ?? "—";
}}
renderActions={(row) => (
<button type="button" className="ub-table-action-btn" onClick={() => edit(row)}>
Edit
</button>
)}
/>Status helpers: ub-status, ub-status--active, ub-status--inactive, ub-status--pending.
Pagination
<Grid
columns={columns}
rows={rows}
pageSize={20}
pageSizeOptions={[10, 20, 50, 100]}
pageSizeMin={5}
pageSizeMax={200}
pageSizeStep={5}
showPageSizeCustom
/>- Rows = presets
- Custom = any value in min–max (desktop; auto-hidden when narrow)
- Narrow: one row —
‹· pages ·›· Rows · short1/1 · N
Remote / API data
// Built-in fetch
<Grid data="/api/members" />
// Parent fetch
function MembersGrid() {
const [rows, setRows] = useState([]);
useEffect(() => {
fetch("/api/members").then((r) => r.json()).then(setRows);
}, []);
return <Grid columns={columns} rows={rows} />;
}Filtering, sorting, and paging are client-side.
App wrapper tips
<div
className="my-grid"
style={{
padding: 24,
width: "100%",
maxWidth: "100%",
minWidth: 0,
boxSizing: "border-box",
textAlign: "left",
}}
>
<Grid columns={columns} rows={rows} />
</div>Avoid #root { overflow-x: hidden; } — it can clip search / pagination.
Built-in toolbar features
- Search (icon inside input)
- Freeze
- View
- Filter columns
- Column + Value
- Clear
- Header sort (asc → desc → none)
- Pagination
Exports
import {
Grid,
GridShell,
GridToolbar,
GridSection,
Pagination,
PaginationSection,
ColumnPicker,
} from "@designdaddy/datagrid";Most apps only need Grid.
Utils (no UI): filterRows, sortRows, columnsFromRows, normalizeDataPayload, themeToCssVars, isDataUrl, …
Local pack / publish
npm run build -w @designdaddy/datagrid
cd packages/datagrid
npm pack
# then: npm install /path/to/designdaddy-datagrid-0.1.0.tgzRun the docs UI
cd designdaddy
npm install
npm run dev:docsOpen http://localhost:3002 (or the URL Vite prints).
| Path | What you see |
|------|----------------|
| /docs/datagrid/overview | Overview + Desktop/Tablet/Mobile demo + Quick start + Styling |
| /docs/datagrid/grid | Full Grid — props by feature + Styling |
| /docs/datagrid/grid-shell | Shell composition demo + Styling |
| /docs/datagrid/grid-toolbar | Toolbar only (no table/pagination) + Styling |
| /docs/datagrid/grid-section | Table only + Styling |
| /docs/datagrid/filters | ColumnPicker only + Styling |
| /docs/datagrid/pagination | Pagination only + Styling |
| /docs/datagrid/utils | Helpers (no UI) |
| /docs/datagrid/example-* | Focused examples |
Demos include a Desktop (1100px) / Tablet (768px) / Mobile (390px) switcher — scroll horizontally if needed; you do not have to resize the browser.
npm run build -w @designdaddy/docs
npm run preview -w @designdaddy/docsFolder layout
packages/datagrid/
package.json
README.md
src/
index.js
Grid/ # facade you usually import
GridShell/ # layout slots
GridToolbar/ # filters strip
GridSection/ # table region
Filters/ # ColumnPicker
Pagination/
DataTable/ # internal cell helpers (used by GridSection)
styles/
utils/
icons.jsxNotes for packaging
"exports"points atdist/— runnpm run buildbefore publish.- CSS ships via
dist/index.css(imported from the JS entry). react/react-domare peerDependencies.- Consumers supply
columns/rowsordata— no sample rows in the package.
