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";import { Grid } from "@1771technologies/lytenyte-pro";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
childrenprop isundefined. In this mode, theGridcomponent 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
childrento theGridcomponent. In this mode, you must render each grid component part to create a valid view. The component parts are defined on theGridcomponent itself. For example, you can render the header usingGrid.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> <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.RowsContainer> <Grid.RowsTop> {(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.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> {(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.RowsBottom> </Grid.RowsContainer> </Grid.Viewport></Grid><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.
Header
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
divelement and accepts all standarddivprops.
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 ofGrid.Cellelements as children. EachGrid.Cellelement 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.
Next Steps
- Responsive Container: Learn how to configure containers for LyteNyte Grid.
- API Extensions: Extend LyteNyte Grid's base API with custom functions and state.
- Grid Reactivity: Learn how LyteNyte Grid enables declarative reactivity.
