npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@reforgium/data-grid

v3.1.1

Published

reforgium DataGrid component

Readme

@reforgium/data-grid

npm version License: MIT

High-performance data grid for Angular (18+).

@reforgium/data-grid provides a flexible and performant component for displaying large tabular datasets.
It focuses on smooth scrolling, predictable layout, and full control over rendering via templates and signals.

Designed for real-world datasets, not demo tables.


Features

  • Horizontal and vertical scrolling
  • Virtual row rendering (smooth, no jumps)
  • Infinity scroll (loads data when reaching the bottom)
  • Jitter-free fixed (sticky) columns
  • Two-line text clamp (header + body) with ellipsis
  • Declarative column DSL (<re-dg-column>) [NEW in 2.0.0]
  • Column expanders (hidden columns via toggler)
  • Scrollable overlay scrollbar
  • Pinned rows (top and bottom)
  • Custom templates for headers, cells, pinned rows, icons
  • Skeleton loading rows for pagination/infinity [NEW in 2.0.0]
  • Row selection (single / multi)
  • Signals-based API (signal() first)
  • Paginator component [NEW in 1.1.0]
  • Column manager dropdown [NEW in 2.0.0]

Installation

npm install @reforgium/data-grid
import { DataGrid } from '@reforgium/data-grid';
import { DataGridPaginator } from '@reforgium/data-grid/paginator';
import { DataGridColumnManager } from '@reforgium/data-grid/column-manager';

@Component({ imports: [DataGrid, DataGridPaginator] })
export class SomeComponent {
}

<re-data-grid
  mode="infinity"
  [data]="users"
  [columns]="columns"
  [pageSize]="50"
  [loading]="loading"
  (pageChange)="loadMore($event)"
/>

For small and medium lists, data remains the simplest contract.

For server-paged lists, you can now pass a page-oriented source instead:

import type { GridPagedDataSource } from '@reforgium/data-grid';

type User = { id: number; name: string };

declare const usersSource: GridPagedDataSource<User>;
<re-data-grid
  mode="infinity"
  [source]="usersSource"
  [columns]="columns"
  [pageSize]="20"
/>

In source mode, the grid keeps its own internal page buffer for infinity scrolling, so the parent does not need to maintain one ever-growing accumulated data[].

Important:

  • The grid does not auto-fetch on mount in source mode.
  • The parent should first apply filters / route params / query state and then call the source explicitly (fetch(...), updatePage(0), etc.).
  • After the source is initialized, the grid may call source.updatePage(...) for user-driven page changes and infinity buffering.
  • If the source exposes updatePageSize(...), the grid uses it when the page size changes.
  • If the source exposes sort, updateSort(...), or updateSorts(...), the grid keeps sort state in sync and can delegate user sorting directly to the source.
  • Infinity buffering uses prefetchMode: 'sequential' by default for compatibility with latest-wins sources such as PagedQueryStore.
  • Set prefetchMode: 'parallel' only for sources that safely support concurrent updatePage(...) calls.

PagedQueryStore from @reforgium/statum fits this contract directly, so [source]="store" is enough for the common server-table case.

Configuration

Global defaults provider

You can override default input values for all grid instances via DI:

import { provideDataGridDefaults } from '@reforgium/data-grid';

export const appConfig: ApplicationConfig = {
  providers: [
    provideDataGridDefaults({
      mode: 'pagination',
      hasIndexColumn: true,
      resizable: true,
      pageSize: 50,
      translations: {
        indexColumnHeader: 'No.',
      },
    }),
  ],
};

Supported default fields:

  • mode
  • hasIndexColumn
  • selection
  • pageSize
  • resizable
  • rowHeight
  • headerHeight
  • height
  • virtualBuffer
  • loadingMode
  • deferContent
  • deferHeader
  • deferPinned
  • deferCells
  • pageStartFromZero
  • translations
  • debounce

translations supports: emptyState, itemsPerPageLabel, nextPageLabel, prevPageLabel, indexColumnHeader.

Global header text resolver

If your app keeps column headers as i18n keys, you can resolve them globally via DI without wiring headerTemplate for every table.

  • provideDataGridHeaderTextResolver(...) registers (text, ctx) => string | Signal<string>
  • The resolver applies to plain column headers and header-group titles
  • headerTemplate / titleTemplate still win for markup, but receive already-resolved text in $implicit
  • provideDataGridHeaderTextResolverWithParent(...) is available for advanced parent-resolver composition
import { inject } from '@angular/core';
import { provideDataGridHeaderTextResolver } from '@reforgium/data-grid';
import { LangService } from '@reforgium/presentia';

export const appConfig: ApplicationConfig = {
  providers: [
    provideDataGridHeaderTextResolver(() => {
      const lang = inject(LangService);

      return (text) => (text.includes('.') ? lang.observe(text) : text);
    }),
  ],
};
columns = [
  { key: 'name', header: 'users.columns.name' },
  { key: 'email', header: 'users.columns.email' },
];

Type registries (global + local)

You can register type-based transformers and renderers globally via DI.

  • provideDataGridTypeTransformers(...) registers type -> (row, ctx) => value
  • provideDataGridTypeRenderers(...) registers type -> TemplateRef
  • reDataGridTypeCell="..." registers an instance-local renderer (only for that grid instance)
import {
  provideDataGridTypeTransformers,
} from '@reforgium/data-grid';

export const routes: Routes = [
  {
    path: 'users',
    providers: [
      provideDataGridTypeTransformers({
        money: (_row, ctx) => `$${Number(ctx.value ?? 0).toLocaleString('en-US')}`,
      }),
    ],
    loadComponent: () => import('./users.page').then((m) => m.UsersPage),
  },
];
columns = [
  { key: 'salary', header: 'Salary', type: 'money' },
];
<re-data-grid [data]="users" [columns]="columns">
  <!-- Local renderer overrides global renderer for this grid only -->
  <ng-template reDataGridTypeCell="money" let-value="value" let-row="row">
    <b>{{ value }}</b> <small>{{ row.status }}</small>
  </ng-template>
</re-data-grid>

Renderer precedence:

  1. column.renderTemplate
  2. local reDataGridTypeCell
  3. DI provideDataGridTypeRenderers(...)
  4. built-in renderer (date, number, index, ...)
  5. default text renderer

Value transform precedence:

  1. column.transformer(row, ctx)
  2. DI provideDataGridTypeTransformers(...)
  3. default value pipeline

ctx includes: value, col, index, type.

Type/value callback context (ctx) and pinned rows:

  • value(row, ctx) receives: ctx.col, ctx.index, ctx.isPinned
  • transformer(row, ctx) receives: ctx.value, ctx.col, ctx.index, ctx.type, ctx.isPinned
  • Cell template contexts (including reDataGridTypeCell, reDataGridCell) expose isPinned
columns = [
  {
    key: 'code',
    header: 'Code',
    value: (row, ctx) => (ctx.isPinned ? 'PINNED' : row.code),
  },
  {
    key: 'salary',
    header: 'Salary',
    type: 'money',
    transformer: (row, ctx) => (ctx.isPinned ? `PIN: ${ctx.value}` : ctx.value),
  },
];
<ng-template reDataGridTypeCell="money" let-value="value" let-isPinned="isPinned">
  <b>{{ value }}</b>
  @if (isPinned) {
    <small>PIN</small>
  }
</ng-template>

Inputs

| Parameter | Type | Default | Description | | |--------------------|--------------------------------------------------|--------------------|-------------------------------------------------------------------------------------------------|------------------| | data | T[] | [] | Data array to render | | | source | GridPagedDataSource<T> \| null | null | Optional page-oriented source for pagination/infinity flows | [NEW in 3.x] | | columns | GridColumn<T>[] | [] | Programmatic column configuration (used when no declarative columns are provided) | | | headerGroups | GridHeaderGroup<T>[] | [] | Optional top header row groups (from..to) for 2-level headers | [NEW in 2.0.0] | | pinnedRows | GridPinnedRow<T>[] | [] | Top / bottom pinned rows | | | isRowSticky | (row: T, index: number) => boolean | undefined | Predicate that marks rows as sticky at the top | | | isRowDisabled | (row: T, index: number) => boolean | undefined | Predicate that marks rows as disabled (no select/click/context/double-click interactions) | | | getRowTemplate | (row: T, index: number) => TemplateRef \| null | undefined | Optional resolver for custom row template | | | sortMode | 'single' | 'multi' | 'single' | Sorting mode for header clicks | | | pageSize | number | 20 | Number of items per page (pagination/infinity modes) | | | pageStartFromZero | boolean | true | If true, page indexing starts from 0, otherwise from 1 | | | hasIndexColumn | boolean | false | If true, an index column will be shown | | | selection | GridSelection<T> | { mode: 'none' } | Row selection configuration | | | rowHeight | number | 40 | Fixed height of each row in pixels | [FIX in 1.1.0] | | virtualBuffer | number | 8 | Extra rows to render above/below viewport to reduce flickering during scrolling | | | lockVerticalScroll | boolean | false | Locks vertical scrolling while keeping horizontal scroll enabled | | | height | number \| 'full' \| 'default' | 'default' | Grid height configuration (number for px, 'full' for 100%, 'default' for CSS variable) | | | loading | boolean | false | Displays loading state template when true | | | loadingMode | 'spinner' \| 'skeleton' | 'spinner' | Loading rendering mode (spinner shows centered spinner, skeleton enables row skeleton mode) | [NEW in 2.0.0] | | deferContent | boolean | true | Defers rendering of main content (code-splitting) | | | deferHeader | boolean | false | Defers rendering of header (groups + columns) | | | deferPinned | boolean | false | Defers rendering of pinned rows (top + bottom) | | | deferCells | boolean | false | Defers rendering of cell content | | | rowKey | DataKey<T> \| ((item: T) => string \| number) | undefined | Property name or function to resolve unique row key | |

When selection mode is 'single' or 'multi', provide a key (data property) and optionally defaultSelected.

Selection mode guidance:

  • Prefer using row selection with mode="pagination" for clearer UX and predictable “select all” behavior.
  • In mode="infinity", selection typically applies only to currently loaded rows, so use it only when that behavior is acceptable.

Data source guidance:

  • Use data when the parent already owns the full rendered collection.
  • Use source for server-driven pagination or infinity flows where the parent should expose only the current page.
  • In mode="infinity" + source, the grid uses totalElements for scroll height when available and stores loaded page chunks internally instead of forcing the parent to append rows into one large array.
  • GridPagedDataSource.totalElements is optional for infinity sources. If omitted, the grid keeps requesting pages until it receives a short page (items.length < pageSize) and then treats that page as the end of the dataset.
  • In source mode, initialize the source from the parent after all filters / params are ready; the grid does not perform an automatic first fetch on mount.
  • GridPagedDataSource.error is optional and can be used by consumers that want to project source-level error UI near the grid.
  • GridPagedDataSource.sort, updateSort(...), and updateSorts(...) let the grid use source-owned sort state instead of forcing a separate parent bridge.
  • GridPagedDataSource.prefetchMode defaults to sequential; keep that default for PagedQueryStore and similar latest-wins sources.
  • Use prefetchMode: 'parallel' only when the source supports multiple concurrent updatePage(...) requests without canceling each other.
  • When source.version changes or a new source is assigned, the grid invalidates its internal buffer immediately; any in-flight updatePage() responses that arrive after invalidation are discarded and do not overwrite the fresh buffer.

Feature lazy-loading behavior (runtime import()):

  • Selection feature is loaded when selection.mode !== 'none'.
  • Sticky feature is loaded when isRowSticky is provided.
  • Tooltip feature is loaded when at least one column has tooltip.
  • Overlay-scroll feature is loaded on first scroll lifecycle usage.

Loading behavior:

  • loadingMode="spinner": shows centered spinner over grid content.
  • loadingMode="skeleton" + mode="infinity" + loading=true: appends 4 skeleton rows at the end of the current data.
  • loadingMode="skeleton" + mode="pagination" + loading=true + empty data: renders 4 skeleton rows in the table body.

Sticky rows:

  • Provide isRowSticky to mark rows that should stick to the top during scroll.
  • You can customize the sticky row rendering with reDataGridStickyRow template.

<re-data-grid [data]="items" [columns]="columns" [isRowSticky]="isSticky">
  <ng-template reDataGridStickyRow let-row let-index="index">
    <div class="my-sticky-row">{{ index + 1 }}. {{ row.name }}</div>
  </ng-template>
</re-data-grid>

Row templates:

  • Provide getRowTemplate to select a custom row template per row.
  • Use reDataGridRow as a default row template.

<re-data-grid [data]="items" [columns]="columns" [getRowTemplate]="rowTpl">
  <ng-template reDataGridRow let-index="index">
    <div class="my-row">{{ index + 1 }}. {{ row.name }}</div>
  </ng-template>
</re-data-grid>

Outputs

| Event | Type | Description | | |-----------------|-------------------------------|--------------------------------------------------------|------------------| | cellClick | GridCellClickEvent<T> | Emitted when a cell is clicked (includes native event) | [UPD in 2.0.0] | | cellContext | GridCellContextEvent<T> | Emitted on cell context menu | [NEW in 2.0.0] | | cellDoubleClick | GridCellDoubleClickEvent<T> | Emitted when a cell is double-clicked | [NEW in 2.0.0] | | rowClick | GridRowClickEvent<T> | Emitted when a row is clicked (includes native event) | [UPD in 2.0.0] | | rowContext | GridRowContextEvent<T> | Emitted on row context menu | [NEW in 2.0.0] | | rowDoubleClick | GridRowDoubleClickEvent<T> | Emitted when a row is double-clicked | [NEW in 2.0.0] | | columnResizeEnd | GridColumnResizeEndEvent<T> | Emitted when a column resize drag ends (key + px width); use to persist widths | [NEW in 3.1.0] | | sortChange | GridSortEvent<T> | Emitted when single-sort order changes | | | multiSortChange | GridMultiSortEvent<T> | Emitted when multi-sort order changes | [NEW in 2.2.0] | | pageChange | GridPageChangeEvent | Emitted when requesting data for a new page | | | selectChange | GridSelectEvent<T> | Emitted when selected rows change | |

Notes:

  • A cell click also triggers the row click event (bubbling), so listen to one or stop propagation if needed.
  • In source mode, sort events are still emitted, but if the source implements sort hooks the grid also updates the source directly.
  • selectChange is emitted not only on user interaction but also when the grid automatically prunes selected keys after a data update removes rows that were previously selected. Always treat selectChange as the authoritative selected state.

Public API methods

DataGrid exposes imperative helpers via component instance (e.g. @ViewChild(DataGrid)):

  • clearSelection() - clears current selection and emits selectChange
  • selectAllLoaded() - selects currently loaded rows (multi mode) and emits selectChange
  • resetSort() - resets sort state and emits sort events
  • setSort(event) - applies single-sort state and emits sort events
  • setMultiSort(items) - applies multi-sort state and emits sort events

GridPagedDataSource reference

The optional source input accepts a page-oriented contract:

| Field / method | Type | Description | |---|---|---| | items | Signal<T[]> | Current page items | | loading | Signal<boolean> | Source loading state | | error | Signal<unknown \| null> | Optional source error state | | page | number | Current page index | | pageSize | number | Current page size | | totalElements | number \| undefined | Optional total size | | sort | ReadonlyArray<GridSortItem<T>> \| undefined | Optional source-owned sort state | | version | Signal<number> \| undefined | Dataset reset marker for local buffer invalidation | | updatePage(page) | (page: number) => Promise<unknown> | Load a page | | updatePageSize(size) | ((size: number) => Promise<unknown>) \| undefined | Optional page-size handler | | updateSort(sort) | ((sort?: GridSortItem<T> \| null) => Promise<unknown>) \| undefined | Optional single-sort handler | | updateSorts(sort) | ((sort?: ReadonlyArray<GridSortItem<T>> \| null) => Promise<unknown>) \| undefined | Optional multi-sort handler | | prefetchMode | 'sequential' \| 'parallel' \| undefined | Infinity prefetch strategy |

This contract is intentionally grid-like, not statum-specific, but PagedQueryStore already matches it well enough to be used directly.

GridColumn reference

columns accepts GridColumn<T>[], where GridColumn<T> is a union of three column variants:

  • default column (type + optional typeParams)
  • value column (value + required track)
  • template column (renderTemplate + required track)

Common (base) fields:

| Field | Type | Description | | |-------------------------|-----------------------------------------------------------------------------|----------------------------------------------------------------------------------------|------------------| | sortKey | string | Alternative key used for sorting when display key differs from sortable data. | | | sticky | 'left' \| 'right' \| true | Keeps the column fixed while horizontally scrolling. true pins to the left. | | | expandBy | DataKey<T> | Data key that controls expand/collapse behavior for the column. | | | flex | number | Flex grow factor for width distribution in flexible layouts. | | | minWidth / maxWidth | number | Column width limits in pixels. | | | resizable | boolean | Enables drag resize from header. false disables resize handle for the column. | | | cellClass | string \| ((row: T) => string) | Static CSS class or resolver per row. | | | tooltip | true \| string \| ((row: T) => string) \| TemplateRef<GridTooltipContext> | Popover tooltip content shown on cell hover. true uses the cell value automatically. | [NEW in 2.2.0] |

Renderer-specific fields:

| Variant | Fields | Notes | |--------------------------|----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------| | Built-in / type renderer | type?: GridBuiltInCellType \| string, typeParams?: any, defaultValue?: any | Built-in types are plain/date/number/index/checkbox; custom string types can be handled by local/DI type registries. | | Value renderer | value: (row: T, ctx) => string \| number, track: (row: T) => string | track is required for stable row identity and efficient updates. ctx includes col, index, isPinned. | | Template renderer | renderTemplate: TemplateRef<...>, track: (row: T) => string | track is required when rendering through Angular templates. |

Cell text wrapping/clamp behavior:

  • Default header text and default body renderers (plain, date, number, index) are limited to 2 lines with ellipsis.
  • Template-based renderers (renderTemplate, reDataGridCell, reDataGridTypeCell, etc.) are not clamped by default and are fully controlled by your template styles.
  • Cell template contexts also include isPinned so pinned top/bottom rows can be rendered differently.

Declarative columns [NEW in 2.0.0]

You can define columns directly in markup via <re-dg-column>, then the grid will normalize them to GridColumn<T> internally.


<re-data-grid [data]="items" [rowKey]="'id'">
  <re-dg-column key="name">
    <ng-template reHeader>Name</ng-template>
    <ng-template reCell let-row="row">{{ row.name }}</ng-template>
  </re-dg-column>

  <re-dg-column key="age" sortKey="age">
    <ng-template reHeader>Age</ng-template>
    <ng-template reCell let-row="row">{{ row.age }}</ng-template>
  </re-dg-column>

  <re-dg-column key="extra" sticky="left" expandBy="age" disabled>
    <ng-template reHeader>Extra</ng-template>
    <ng-template reCell let-row="row">{{ row.extra }}</ng-template>
  </re-dg-column>
</re-data-grid>

Notes:

  • If at least one <re-dg-column> is present, declarative columns are used as the base configuration.
  • When used with column manager state, visible, disabled, and sticky are taken from state when defined.
  • reHeader maps to headerTemplate.
  • reCell receives value as $implicit, and the row is available as let-row="row" via RenderTemplateData.
  • Most GridColumn<T> fields are available as <re-dg-column> inputs (sortKey, sticky, expandBy, disabled, width, minWidth, maxWidth, flex, resizable, align, cellClass, type, typeParams, defaultValue, value, track, tooltip).
  • sticky accepts 'left' | 'right'; legacy true maps to 'left' for backward compatibility.

Tooltip

tooltip is opt-in per column and shows a popover on cell hover.

columns = [
  { key: 'name', header: 'Name', tooltip: true }, // auto tooltip from cell value
  { key: 'email', header: 'Email', tooltip: (row) => row.email },
  { key: 'status', header: 'Status', tooltip: 'User status' },
];

Template tooltip:


<ng-template #tip let-row let-col="col" let-index="index" let-value="value">
  <div><b>{{ col.header }}</b> #{{ index + 1 }}</div>
  <div>{{ value }}</div>
</ng-template>

<re-dg-column key="email" [tooltip]="tip"></re-dg-column>

Tooltip template context (GridTooltipContext):

  • $implicit / row
  • col
  • index
  • value

Header groups [NEW in 2.0.0]

Use headerGroups to render an optional top header row (2-level header layout) above regular column headers.

| Field | Type | Description | |-----------------|-----------------------------------|------------------------------------------------------------------| | key | string | Unique id of the header group. | | from | DataKey<T> | Start column key (inclusive). | | to | DataKey<T> | End column key (inclusive). If omitted, group covers one column. | | title | string | Plain text title for the group. | | titleTemplate | TemplateRef<HeaderTemplateData> | Template-based title for the group. | | align | 'left' \| 'center' \| 'right' | Optional group title alignment (plain title variant). |

Notes:

  • Groups are normalized against visible columns only.
  • Overlapping or invalid ranges are ignored during normalization.
  • Columns not covered by groups are automatically placed into technical spacer groups to keep width alignment.

Templates

| Directive | Parameters | Description | | |------------------------|-----------------------------------|---------------------------------------------------|------------------| | reHeader | - | Declarative header template inside re-dg-column | [NEW in 2.0.0] | | reCell | let-row | Declarative cell template inside re-dg-column | [NEW in 2.0.0] | | reDataGridHeader | key: string | Template for specific column header by key | | | reDataGridCell | key: string | Template for specific column cell by key | [NEW in 1.1.0] | | reDataGridTypeCell | type: string | Template for cells of a specific column type | | | reDataGridEmpty | - | Template for the empty state (no data) | | | reDataGridLoading | - | Template for the loading state | | | reDataGridSortIcon | order: GridSortOrder | undefined | Template for custom sorting icon | [NEW in 1.1.0] | | reDataGridExpanderIcon | expanded: boolean | Template for custom expander icon | [NEW in 1.1.0] |

CSS Variables

Layout / Appearance:

  • --re-data-grid-min-height - component min height (200px)
  • --re-data-grid-height - component height (400px)
  • --re-data-grid-rounded - border radius (var(--radius-md, 6px))
  • --re-data-grid-separator-color - outer border color (var(--border-color))
  • --re-data-grid-separator - outer border (1px solid var(--re-data-grid-separator-color))
  • --re-data-grid-surface - table background (#fff)
  • --re-data-grid-active - active color (#2a90f4)

Empty / Loading States:

  • --re-data-grid-empty-color - empty text color (#777)
  • --re-data-grid-empty-surface - empty text background (transparent)
  • --re-data-grid-loading-color - loading indicator color (#444)
  • --re-data-grid-loading-surface - loading spinner (rgba(255,255,255,0.5))
  • --re-data-grid-spinner-size - spinner size (2rem)
  • --re-data-grid-spinner-width - spinner ring thickness (0.25rem)
  • --re-data-grid-spinner-track-color - spinner track color (rgba(0, 0, 0, 0.12))
  • --re-data-grid-skeleton-width - skeleton line width (100%)
  • --re-data-grid-skeleton-height - skeleton line height (100%)
  • --re-data-grid-skeleton-rounded - skeleton border radius (var(--re-data-grid-rounded, 0.75rem))
  • --re-data-grid-skeleton-line - skeleton base color (#e7ebf0)
  • --re-data-grid-skeleton-shine - skeleton highlight color (rgba(255, 255, 255, 0.8))

Tooltip:

  • --re-data-grid-tooltip-surface - tooltip background (#0f172a)
  • --re-data-grid-tooltip-color - tooltip text color (#f8fafc)
  • --re-data-grid-tooltip-radius - tooltip radius (0.5rem)
  • --re-data-grid-tooltip-padding - tooltip padding (0.4rem 0.6rem)
  • --re-data-grid-tooltip-shadow - tooltip shadow (0 8px 24px rgba(15, 23, 42, 0.25))
  • --re-data-grid-tooltip-z - tooltip z-index (60)

Note: for --re-data-grid-skeleton-height it's usually better to use percentages (for example 60% / 70%) so the line scales naturally with row height.

Scrollbar:

  • --re-data-grid-scrollbar-size - track size (4px)
  • --re-data-grid-scrollbar-offset - inner offset (2px)
  • --re-data-grid-scrollbar-track-rounded - track radius (0.25rem)
  • --re-data-grid-scrollbar-track-surface - track surface (transparent)
  • --re-data-grid-scrollbar-thumb-size - thumb size (8px)
  • --re-data-grid-scrollbar-thumb-color - thumb color (rgba(0,0,0,0.25))
  • --re-data-grid-scrollbar-thumb-active-color - thumb active color (rgba(0,0,0,0.45))
  • --re-data-grid-scrollbar-thumb-rounded - thumb radius (var(--re-data-grid-scrollbar-track-rounded))

Header:

  • --re-data-grid-header-rounded - header corner radius (var(--re-data-grid-rounded))
  • --re-data-grid-header-surface - header area background (#fff)
  • --re-data-grid-header-body-gap - gap between header and body (0px)
  • --re-data-grid-header-row-height - main header row height (40px)
  • --re-data-grid-header-row-separator-color - main header separator color (#ccc)
  • --re-data-grid-header-row-separator - main header separator (1px solid var(--re-data-grid-header-row-separator-color))
  • --re-data-grid-header-group-row-height - group header row height (var(--re-data-grid-header-row-height))
  • --re-data-grid-header-group-row-separator-color - group header separator color (var(--re-data-grid-header-row-separator-color))
  • --re-data-grid-header-group-row-separator - group header separator (1px solid var(--re-data-grid-header-group-row-separator-color))

Header Cells:

  • --re-data-grid-header-cell-font-weight - font weight (600)
  • --re-data-grid-header-cell-font-size - font size (0.8rem)
  • --re-data-grid-header-cell-color - text color (#000)
  • --re-data-grid-header-cell-surface - cell background (#fafafa)
  • --re-data-grid-header-cell-line-height - header text line-height for clamp (1.2)
  • --re-data-grid-header-cell-max-lines - header max text lines (2)
  • --re-data-grid-header-group-cell-font-weight - group font weight (var(--re-data-grid-header-cell-font-weight))
  • --re-data-grid-header-group-cell-font-size - group font size (var(--re-data-grid-header-cell-font-size))
  • --re-data-grid-header-group-cell-color - group text color (var(--re-data-grid-header-cell-color))
  • --re-data-grid-header-group-cell-surface - group cell background (var(--re-data-grid-header-cell-surface))

Footer:

  • --re-data-grid-footer-separator-color - separator color (#ccc)
  • --re-data-grid-footer-separator - separator line (1px solid var(--re-data-grid-footer-separator-color))
  • --re-data-grid-footer-surface - footer background (#fff)

Rows:

  • --re-data-grid-row-separator-color - row separator color (#bbb)
  • --re-data-grid-row-separator - separator line (1px solid var(--re-data-grid-row-separator-color))
  • --re-data-grid-row-odd-surface - odd rows background (var(--re-data-grid-cell-surface))
  • --re-data-grid-row-hover-surface - hover rows background (var(--re-data-grid-cell-surface))
  • --re-data-grid-row-hover-color - hover rows text color (var(--re-data-grid-cell-color))
  • --re-data-grid-row-hover-rounded - hover row corner radius (0px)

Columns:

  • --re-data-grid-column-separator-color - column divider color (transparent)
  • --re-data-grid-column-separator - column divider (1px solid var(--re-data-grid-column-separator-color))
  • --re-data-grid-column-odd-surface - odd column background (var(--re-data-grid-cell-surface))

Cells:

  • --re-data-grid-cell-paddings - inner paddings (0.4rem 0.625rem)
  • --re-data-grid-cell-font-weight - font weight (400)
  • --re-data-grid-cell-font-size - font size (0.75rem)
  • --re-data-grid-cell-color - text color (#000)
  • --re-data-grid-cell-surface - cell background (#fff)
  • --re-data-grid-cell-line-height - body text line-height for clamp (1.2)
  • --re-data-grid-cell-max-lines - body max text lines (2)

Checkbox:

  • --re-data-grid-checkbox-size - checkbox size (20px)
  • --re-data-grid-checkbox-stroke - checkbox border width (2px)
  • --re-data-grid-checkbox-border - checkbox border color (var(--border-color, #9aa3af))
  • --re-data-grid-checkbox-tick - checkbox tick color (var(--surface-neutral, #fff))
  • --re-data-grid-checkbox-surface - checkbox background (var(--surface-neutral, #fff))
  • --re-data-grid-checkbox-active-color - checkbox active color (var(--primary-color, #2563eb))

Focus Ring:

  • --re-data-grid-focus-ring-color - keyboard focus outline color (color-mix(in srgb, var(--primary-color, #2a90f4) 55%, transparent))
  • --re-data-grid-focus-ring-width - keyboard focus outline width (2px)
  • --re-data-grid-focus-ring-offset - keyboard focus outline offset (-2px)

Sticky Cells:

  • --re-data-grid-sticky-header-cell-surface - sticky header cell background (#fff)
  • --re-data-grid-sticky-cell-surface - sticky body cell background (#fdfdfd)
  • --re-data-grid-sticky-cell-row-odd-surface - sticky odd rows background (#fdfdfd)
  • --re-data-grid-sticky-cell-left-shadow - left shadow (2px 0 2px rgba(0,0,0,0.03))
  • --re-data-grid-sticky-cell-right-shadow - right shadow (-2px 0 2px rgba(0,0,0,0.03))

Pinned Sections:

  • --re-data-grid-pinned-surface - pinned sections background (#fcfcfc)
  • --re-data-grid-pinned-separator-color - separator color (#eee)
  • --re-data-grid-pinned-separator - separator line (1px solid var(--re-data-grid-pinned-separator-color))

Misc:

  • --re-data-grid-expander-color - expander indicator color (var(--primary-color, currentColor))
  • --re-data-grid-expanded-color - expanded column color (var(--re-data-grid-cell-color, #000))
  • --re-data-grid-expanded-surface - expanded column background (var(--re-data-grid-cell-surface, #fff))

Paginator [NEW in 1.1.0]

The paginator component is used to display a page selector and total count.

Inputs

| Parameter | Type | Default | Description | |-----------------|------------|-------------------|----------------------------------------| | current | number | 0 | Current page | | pageSize | number | 0 | Number of items per page | | totalElements | number | 0 | Total number of elements | | maxShowPages | number | 7 | Maximum number of page buttons to show | | showFirstLast | boolean | false | Show "First" and "Last" buttons | | showPerPage | boolean | false | Show page-size dropdown | | pageSizeOptions | number[] | [10,20,50,100] | Options for per-page dropdown | | perPageLabel | string | Items per page: | Label near dropdown | | firstLabel | string | First | Fallback label for first-page button | | lastLabel | string | Last | Fallback label for last-page button |

Outputs

| Event | Type | Description | |----------------|----------|----------------------------------------------------------------------| | pageChange | number | Emitted when the page changes. Returns the new page index (0-based). | | pageSizeChange | number | Emitted when per-page value changes. |

Paginator templates

You can customize first/last controls with ng-template:


<re-data-grid-paginator
  showFirstLast
  showPerPage
  [current]="page"
  [pageSize]="20"
  [pageSizeOptions]="[10, 20, 50]"
  [totalElements]="total"
  (pageChange)="page = $event"
  (pageSizeChange)="pageSize = $event"
>
  <ng-template reDataGridPaginatorFirst let-targetPage let-disabled="disabled">
    <span [style.opacity]="disabled ? 0.5 : 1">&lt;&lt; First</span>
  </ng-template>

  <ng-template reDataGridPaginatorPage let-label="label" let-active="active">
    <span [style.fontWeight]="active ? 700 : 400">{{ label }}</span>
  </ng-template>

  <ng-template reDataGridPaginatorLast let-targetPage let-disabled="disabled">
    <span [style.opacity]="disabled ? 0.5 : 1">Last &gt;&gt;</span>
  </ng-template>
</re-data-grid-paginator>

Template contexts:

  • reDataGridPaginatorFirst / reDataGridPaginatorLast: $implicit (target page), current, total, disabled
  • reDataGridPaginatorPage: $implicit / page (0-based index), label (1-based display), current, total, active

CSS Variables

First / Last controls:

  • --re-data-grid-paginator-edge-min-width - min width (2.5rem)
  • --re-data-grid-paginator-edge-height - control height (var(--re-data-grid-paginator-page-size))
  • --re-data-grid-paginator-edge-paddings - control paddings (0 0.625rem)
  • --re-data-grid-paginator-edge-border - control border (var(--re-data-grid-paginator-page-border))
  • --re-data-grid-paginator-edge-rounded - control border radius (var(--re-data-grid-paginator-page-rounded))
  • --re-data-grid-paginator-edge-surface - control background (var(--re-data-grid-paginator-page-surface))
  • --re-data-grid-paginator-edge-color - control text color (var(--re-data-grid-paginator-page-color))
  • --re-data-grid-paginator-edge-font-size - control font size (var(--re-data-grid-paginator-page-font-size))
  • --re-data-grid-paginator-edge-hover-surface - hover background (var(--re-data-grid-paginator-page-hover-surface))
  • --re-data-grid-paginator-edge-hover-color - hover text color (var(--re-data-grid-paginator-page-hover-color))
  • --re-data-grid-paginator-edge-disabled-opacity - disabled opacity (0.5)

Layout:

  • --re-data-grid-paginator-gap - gap between paginator elements (0.5rem)

Page:

  • --re-data-grid-paginator-page-size - base page button size, used as default min-width and height (1.75rem)
  • --re-data-grid-paginator-page-min-width - page button min width (var(--re-data-grid-paginator-page-size))
  • --re-data-grid-paginator-page-height - page button height (var(--re-data-grid-paginator-page-size))
  • --re-data-grid-paginator-page-paddings - page button paddings (0 0.5rem)
  • --re-data-grid-paginator-page-border - page button border (1px solid var(--re-data-grid-paginator-separator-color, #e2e8f0))
  • --re-data-grid-paginator-page-separator-color - page button border color (var(--re-data-grid-separator-color, --border-color))
  • --re-data-grid-paginator-page-rounded - page button border radius (var(--re-data-grid-rounded, --radius-md))
  • --re-data-grid-paginator-page-surface - page button background (var(--re-data-grid-surface, white))
  • --re-data-grid-paginator-page-color - page button text color (var(--text-primary, #1e293b))
  • --re-data-grid-paginator-page-font-size - page button font size (0.875rem)

Active Page:

  • --re-data-grid-paginator-page-active-surface - active page button background (var(--re-data-grid-active, #3b82f6))
  • --re-data-grid-paginator-page-active-color - active page button text color (white)

Hover Page:

  • --re-data-grid-paginator-page-hover-surface - page button hover background (var(--re-data-grid-active, #3b82f6))
  • --re-data-grid-paginator-page-hover-color - page button hover text color (white)

Column manager [NEW in 2.0.0]

Column manager is a secondary entrypoint that provides a dropdown UI for reordering, show/hide, and pinning columns.

import { DataGridColumnManager } from '@reforgium/data-grid/column-manager';

<re-data-grid-column-manager
  triggerLabel="Columns"
  [columns]="managedColumns()"
  (columnsChange)="managedColumns.set($event)"
/>

<re-data-grid [columns]="managedColumns()" ... />

Custom trigger via content projection:


<re-data-grid-column-manager
  triggerLabel="Columns"
  [columns]="managedColumns()"
  (columnsChange)="managedColumns.set($event)"
>
  <ng-template reDataGridColumnManagerTrigger>Manage columns</ng-template>
</re-data-grid-column-manager>

Custom column title template (controls stay built-in):

<re-data-grid-column-manager
  [columns]="managedColumns()"
  (columnsChange)="managedColumns.set($event)"
>
  <ng-template reDataGridColumnManagerColumnTitle let-title let-column="column">
    <span class="font-medium">{{ title }}</span>
  </ng-template>
</re-data-grid-column-manager>

reDataGridColumnManagerColumnTitle context:

  • $implicit / title - resolved column title (header || key)
  • column - current GridColumn<T>

Standalone imports for custom trigger/title markers:

import {
  DataGridColumnManager,
  DataGridColumnManagerColumnTitleDirective,
  DataGridColumnManagerTriggerDirective,
} from '@reforgium/data-grid/column-manager';
@Component({
  imports: [
    DataGridColumnManager,
    DataGridColumnManagerTriggerDirective,
    DataGridColumnManagerColumnTitleDirective,
  ],
})
export class ExampleComponent {}

Example with both trigger and column title customizations:

<re-data-grid-column-manager [columns]="managedColumns()" (columnsChange)="managedColumns.set($event)">
  <ng-template reDataGridColumnManagerTrigger>Manage columns</ng-template>
  <ng-template reDataGridColumnManagerColumnTitle let-title>
    <span>{{ title }}</span>
  </ng-template>
</re-data-grid-column-manager>

Inputs:

  • columns: GridColumn<T>[]
  • triggerLabel?: string
  • showAllLabel?: string
  • hideAllLabel?: string
  • controlsVisible?: boolean - hides search and "show all / hide optional" panel
  • searchable?: boolean
  • allowReorder?: boolean
  • allowPin?: boolean
  • allowVisibility?: boolean

Outputs:

  • columnsChange: GridColumn<T>[]

Column manager CSS Variables

  • --re-data-grid-cm-gap - layout gap (0.5rem)
  • --re-data-grid-cm-rounded - panel/trigger radius (0.625rem)
  • --re-data-grid-cm-border - border (1px solid var(--surface-border, #dfe1e6))
  • --re-data-grid-cm-surface - surface color (var(--surface-neutral, #fff))
  • --re-data-grid-cm-muted - muted text color (var(--text-muted, #64748b))
  • --re-data-grid-cm-active - active color (var(--primary-color, #2a90f4))
  • --re-data-grid-cm-shadow - panel shadow (0 12px 30px rgba(15, 23, 42, 0.18))

Theming via CSS Variables

Variables can be overridden globally (:root) or per grid container.

Quick Theming Examples

Dark Theme Example:

.dark .data-grid {
  --re-data-grid-surface: #121416;
  --re-data-grid-header-cell-surface: #161a1d;
  --re-data-grid-cell-color: #eef2f6;
  --re-data-grid-row-separator-color: #262a2e;
  --re-data-grid-column-separator-color: #1e2226;
}

Compact Mode Example:

.compact-grid {
  --re-data-grid-header-row-height: 32px;
  --re-data-grid-cell-font-size: 12px;
  --re-data-grid-cell-paddings: 2px 8px;
}

Custom Template Examples

Cell Template by Type


<ng-template reDataGridTypeCell="link" let-row="row">
  <a [href]="value" target="_blank">{{ value }}</a>
</ng-template>

Cell Template [NEW in 1.1.0]


<ng-template reDataGridCell="fullName" let-row="row">
  <span><b>{{ row.firstName }}</b> {{ row.lastName }}</span>
</ng-template>

Custom Header Template:


<ng-template reDataGridHeader="name" let-value="value">
  <span class="header-with-icon">
    <icon name="user" size="20" />
    {{ value }}
  </span>
</ng-template>

Compatibility

  • Angular: 18+
  • Rendering: ESM
  • Signals: required
  • Zone.js: optional

License

MIT