LyteNyte Grid logo for light mode. Links back to the documentation home page.
Grid

Headless Component Parts

LyteNyte Grid is a headless data grid. Each part of the grid is split into constituent components that you can compose declaratively to form the grid view.

All grid components live on the Grid export from the LyteNyte Grid packages.

import { Grid } from "@1771technologies/lytenyte-core";

The Grid export is the root component of LyteNyte Grid. The Grid component has two modes depending on whether you pass children:

  • Default Mode: Enabled when the children prop is undefined. In this mode, the Grid component renders the headless structure that LyteNyte Grid needs to function in the default setup. This is the recommended mode unless you need to attach custom logic to the individual component parts of the grid.
  • Headless Mode: Enabled when you pass children to the Grid component. In this mode, you must render each grid component part to create a valid view. The component parts are defined on the Grid component itself. For example, you can render the header using Grid.Header.

Info

Only the components needed to form the grid view live under the Grid export. Other LyteNyte Grid exports, such as row data sources, are separate named exports to allow tree shaking. The Grid components are tightly coupled and must be used together under a common Grid component.

The remainder of this guide walks through the individual grid components and then shows a complete demo example. This guide is best read from top to bottom. For a complete working example, see the Getting Started guide.

Grid Anatomy Overview

The code below offers a high-level overview of the individual parts of the grid component anatomy. Treat this example as a general outline of the grid structure.

Apart from the LyteNyte grid’s default mode, Headless Mode has two variants:

  • Headless Mode: A variant that only renders the top-level containers.
  • Full Headless Mode: A fully rendered variant that renders every cell.
<Grid>
<Grid.Viewport>
<Grid.Header />
<Grid.RowsContainer>
<Grid.RowsTop />
<Grid.RowsCenter />
<Grid.RowsBottom />
</Grid.RowsContainer>
</Grid.Viewport>
</Grid>

Grid

The Grid component acts as the root component for all grid elements. It accepts grid props as state and provides its children with the necessary context for LyteNyte Grid to function. The Grid component also exposes the other component parts as properties.

<Grid>{/* Other component parts here. */}</Grid>

Viewport

The Grid.Viewport component creates the element that acts as the overflow parent for the grid. As its name suggests, it defines the visible area of the grid and displays rows and columns based on the scroll position. The viewport automatically sizes to fit its container. See the Responsive Container guide for details on configuring containers for the grid.

Adding Grid.Viewport to the grid component gives us:

<Grid>
<Grid.Viewport />
</Grid>

Grid.Viewport renders a div element. It accepts all standard div props, including className and style. LyteNyte Grid also applies inline styles for grid sizing.

LyteNyte Grid has a single header container, rendered by the Grid.Header component. You can render the Grid.Header component in two ways:

  • Without children: The header component uses the default configuration to render header groups and header cells.
  • With children: The header component accepts a render prop as its children, which gives you an opportunity to add customizations before rendering the header group and header cell elements.

Rendering Grid.Header without children is the most straightforward approach. The Grid.Header component renders a normal div element and accepts all standard div props.

<Grid>
<Grid.Viewport>
<Grid.Header />
</Grid.Viewport>
</Grid>

Header Render Prop

Passing a render prop as the children of the Grid.Header component allows for more fine-grained control over how the header renders. When using this approach, the render prop must return a Grid.HeaderRow, and that component must render the header cells as its children.

The render prop function receives an array of LayoutHeader items. Each LayoutHeader item describes the layout of an individual header cell, such as whether the cell is pinned or whether the cell belongs to a column group, a normal header, or a floating header.

You can use this information to apply custom logic, though the default rendering behavior is usually the best place to start.

<Grid>
<Grid.Viewport>
<Grid.Header>
{(cells) => {
return <Grid.HeaderRow>{/* Header row content */}</Grid.HeaderRow>;
}}
</Grid.Header>
</Grid.Viewport>
</Grid>

Header Row

When using a custom render prop for Grid.Header, the function must return a Grid.HeaderRow element. The total number of header rows equals the maximum column group depth, plus one additional row for the floating row (if enabled), and another row for the column header row.

The Grid.Header component renders a normal div element and accepts all standard div props. The Grid.Header render prop receives an array of header cells as its first argument. You must use these header cells to render the children of the Grid.HeaderRow, as shown in the example below.

<Grid>
<Grid.Viewport>
<Grid.Header>
{(cells) => {
return (
<Grid.HeaderRow>
{cells.map((c) => {
// Return cell content.
})}
</Grid.HeaderRow>
);
}}
</Grid.Header>
</Grid.Viewport>
</Grid>

Header Cells

Within each <Grid.HeaderRow />, you render header cells. These cells represent column headers. LyteNyte Grid provides two header cell components:

  • Grid.HeaderGroupCell: Renders the cell for a column group.
  • Grid.HeaderCell: Renders the cell for a column header or a floating header cell.

The render prop of Grid.Header receives an array of cells to render. Each item in this array represents a single header cell. You must render these cells inside a Grid.HeaderRow element, as shown below.

You can determine which component to render by checking the cell’s kind property. When the cell’s kind is "group", render a Grid.HeaderGroupCell. Otherwise, render a Grid.HeaderCell.

<Grid>
<Grid.Viewport>
<Grid.Header>
{(cells) => {
return (
<Grid.HeaderRow>
{cells.map((c) => {
if (c.kind === "group") return <Grid.HeaderGroupCell cell={c} key={c.idOccurrence} />;
return <Grid.HeaderCell cell={c} key={c.id} />;
})}
</Grid.HeaderRow>
);
}}
</Grid.Header>
</Grid.Viewport>
</Grid>

There are several details to note here. For each cell, choose the appropriate header component base on the kind property. For Grid.HeaderGroupCell, use c.idOccurrence as the key instead of c.id. A column group can be split across the header, so c.id may repeat. React requires keys to be unique within a list, and c.idOccurrence guarantees uniqueness.

Rows Container

Use the Grid.RowsContainer component to render the grid’s rows. Grid.RowsContainer is similar to Grid.Header in that it:

  • Acts as the container for the grid rows.
  • Renders a normal div element and accepts all standard div props.

An updated example is shown below:

<Grid>
<Grid.Viewport>
<Grid.Header />
<Grid.RowsContainer />
</Grid.Viewport>
</Grid>

Top, Center, and Bottom Rows

Grid.RowsContainer is the parent for all grid rows. LyteNyte Grid splits rows into three component sections:

  • Grid.RowsTop: Rows that are pinned to the top of the viewport, after the header.
  • Grid.RowsCenter: Scrollable rows that render after the top rows.
  • Grid.RowsBottom: Rows that are pinned to the bottom of the viewport.

Each section component accepts an optional render prop as children, giving you more fine-grained control over how rows and cells render. Like Grid.Header, this approach provides two ways to render rows:

  • Without a Render Prop: Default mode and the simplest approach that renders the row section without a render prop
  • With a Render Prop: Provide a render prop as children to a row section component to gain more control over row rendering.

The code snippet below demonstrates the default mode structure, so it’s equivalent to rendering <Grid />.

<Grid>
<Grid.Viewport>
<Grid.Header />
<Grid.RowsContainer>
<Grid.RowsTop />
<Grid.RowsCenter />
<Grid.RowsBottom />
</Grid.RowsContainer>
</Grid.Viewport>
</Grid>

Rows Render Prop

The render prop receives a LayoutRow item. Each LayoutRow describes the type of row to render, along with properties such as rowIndex and rowPin. You can use these values to apply row-specific logic.

There are two types of grid row components:

  • Grid.RowFullWidth: Renders a row with a single cell that spans the width of the viewport.
  • Grid.Row: Renders a row that must be provided an array of Grid.Cell elements as children. Each Grid.Cell element renders a single cell in the viewport.

Use the LayoutRow passed to the render prop to determine which row type to render, as shown below.

<Grid>
<Grid.Viewport>
<Grid.Header />
<Grid.RowsContainer>
<Grid.RowsTop />
<Grid.RowsCenter>
{(row) => {
if (row.kind === "full-width") return <Grid.RowFullWidth row={row} />;
return (
<Grid.Row key={row.id} row={row}>
{/* Cell content here */}
</Grid.Row>
);
}}
</Grid.RowsCenter>
<Grid.RowsBottom />
</Grid.RowsContainer>
</Grid.Viewport>
</Grid>

Row Cells

All rows contain cells except for full-width rows. The cells property on LayoutRow is an array of cells that you render using Grid.Cell. Updating the row section component results in the following:

<Grid>
<Grid.Viewport>
<Grid.Header />
<Grid.RowsContainer>
<Grid.RowsTop />
<Grid.RowsCenter>
{(row) => {
if (row.kind === "full-width") return <Grid.RowFullWidth row={row} />;
return (
<Grid.Row key={row.id} row={row}>
{row.cells.map((cell) => {
return <Grid.Cell cell={cell} key={cell.id} />;
})}
</Grid.Row>
);
}}
</Grid.RowsCenter>
<Grid.RowsBottom />
</Grid.RowsContainer>
</Grid.Viewport>
</Grid>

The cells must be rendered as children of a Grid.Row component. Each Grid.Cell renders a div element and accepts all standard div props.

Putting It All Together

Everything so far has focused on assembling the grid. The example below shows a full working setup that combines all the parts covered in the previous sections. The demo includes rows pinned to the top and bottom, column groups, and the full row structure.

Headless Component Parts

Fork code on stack blitzFork code on code sandbox

Next Steps