DocsBlog
  • 1.2.1

  • light

    dark

    system

    Switch mode
  • Cerberus

    Acheron

    Elysium

Get Started
Components
Data Grid
Signals
Styling
Theming

Get Started

OverviewQuick StartColumnsContextTheme

Layout

DimensionsSizingSlotsOverlaysNewToolbarFooterPagination

Features

PinningSortingFilteringNew

Reference

API

On this page

Loading...

Loading...

Loading...

Loading...

On this page

  • Edit this page on Github

Overlays

Learn about the overlays for the Data Grid component.

  • source

Overview

The Data Grid comes with placeholder content overlays that live within the contextual provider. When provided, any component used for the overlay slot will have access to the Data Grid context via the useDataGridContext hook.

You can utilize an overlay by adding the name of the slot as a property to the Data Grid overlays prop. Each overlay slot comes with a default fallback when no custom component is provided.

Overlay slots are placed within the grid viewport which is the body of the Data Grid

Note

All overlays outside of the skeleton will lock the scroll. This includes custom pending overlay components.

No Content

The noContent overlay appears when there are no rows to display in the Data Grid. This can be from providing an empty data Array or being filtered out via a feature or store action.

Custom No Content

Pass a component to the overlays.noContent prop to display a custom overlay.

For convenience, the content is placed within a Center parent that matches the height of the viewport area.

Pending

The pending overlay appears when the Data Grid pending prop is set to true to signify the Grid is in a loading state.

The Data Grid supports 3 loading overlay variants out of the box:

  • skeleton: an animated placeholder of the Data Grid.
  • linear-progress: an indeterminate linear progress bar.
  • circular-progress: a circular loading spinner.

For convenience, the content is placed within an absolutely positioned Box parent that matches the height of the viewport area and sits above the row content.

Skeleton

Pass 'skeleton' to the overlays.pending prop to display a skeleton pending overlay.

Linear

Pass 'linear' to the overlays.pending prop to display a linear-progress pending overlay.

Circular

Pass 'circular' to the overlays.pending prop to display a circular-progress pending overlay.

Custom Loading

Pass a component to the overlays.loading prop to display a custom overlay.

Cell pending with Suspense

For cell level pending states, you can take advantage of Suspense to provide column-level loading states if Promises are used within the Column Definition cell.

This is a natural resource provided by React (and Signals) that doesn't require any additional prop management on the Data Grid.

Note

If using mutations, you need to manually manage the loading state by omitting the Suspense component and instead using the loading prop from useMutation.

💡 "Wow! That was fast after the first click?!"

Yup! The Cerberus signals lib caches querys. Since the state saves a string, the cache is smart enough to know that a string of "success" already exists. Thus, it bypasses the fetch. If the strings where unique (or the data more complex) it would handle it accordingly.

Copy
'use client'

import { DataGrid } from '@cerberus/data-grid'
import { HStack } from 'styled-system/jsx'
import { createFakeQuery } from '../quick-start/data'
import { columns } from '../quick-start/columns.demo'

export function DefaultNoContentDemo() {
  const data = createFakeQuery(0)
  return (
    <HStack h="20rem" w="3/4">
      <DataGrid columns={columns} data={data()} />
    </HStack>
  )
}
Copy
'use client'

import { Corn } from '@carbon/icons-react'
import { DataGrid } from '@cerberus/data-grid'
import { Text } from '@cerberus/react'
import { HStack, VStack } from 'styled-system/jsx'
import { columns } from '../quick-start/columns.demo'
import { createFakeQuery } from '../quick-start/data'

export function NoContentDemo() {
  const data = createFakeQuery(0)
  return (
    <HStack h="20rem" w="3/4">
      <DataGrid
        columns={columns}
        data={data()}
        overlays={{
          noContent: <CustomNoContent />,
        }}
      />
    </HStack>
  )
}

function CustomNoContent() {
  return (
    <VStack color="page.text.100" gap="md">
      <Corn size={24} />
      <Text color="page.text.initial" textStyle="body-md">
        No corn found
      </Text>
    </VStack>
  )
}
Copy
'use client'

import { DataGrid } from '@cerberus/data-grid'
import { HStack } from 'styled-system/jsx'
import { createFakeQuery } from '../quick-start/data'
import { columns } from '../quick-start/columns.demo'

export function SkeletonDemo() {
  const data = createFakeQuery(25)
  return (
    <HStack h="20rem" w="3/4">
      <DataGrid
        pending
        columns={columns}
        data={data()}
        overlays={{
          pending: 'skeleton',
        }}
      />
    </HStack>
  )
}
Copy
'use client'

import { DataGrid } from '@cerberus/data-grid'
import { HStack } from 'styled-system/jsx'
import { createFakeQuery } from '../quick-start/data'
import { columns } from '../quick-start/columns.demo'

export function LinearDemo() {
  const data = createFakeQuery(25)
  return (
    <HStack h="20rem" w="3/4">
      <DataGrid
        pending
        columns={columns}
        data={data()}
        overlays={{
          pending: 'linear',
        }}
      />
    </HStack>
  )
}
Copy
'use client'

import { DataGrid } from '@cerberus/data-grid'
import { HStack } from 'styled-system/jsx'
import { createFakeQuery } from '../quick-start/data'
import { columns } from '../quick-start/columns.demo'

export function CircularDemo() {
  const data = createFakeQuery(25)
  return (
    <HStack h="20rem" w="3/4">
      <DataGrid
        pending
        columns={columns}
        data={data()}
        overlays={{
          pending: 'circular',
        }}
      />
    </HStack>
  )
}
Copy
'use client'

import { DataGrid } from '@cerberus/data-grid'
import { CircularProgress, Text } from '@cerberus/react'
import { useSignal } from '@cerberus/signals'
import { useEffect } from 'react'
import { Center, HStack, VStack } from 'styled-system/jsx'
import { columns } from '../quick-start/columns.demo'
import { createFakeQuery } from '../quick-start/data'

export function CustomDemo() {
  const data = createFakeQuery(25)
  return (
    <HStack h="20rem" w="3/4">
      <DataGrid
        pending
        columns={columns}
        data={data()}
        overlays={{
          pending: <MyCustomLoader />,
        }}
      />
    </HStack>
  )
}

function MyCustomLoader() {
  const [now, setNow] = useSignal<number>(0)

  useEffect(() => {
    const timeout = setInterval(() => {
      setNow((prev) => {
        if (prev === 100) return 0
        return prev + 1
      })
    }, 1000)
    return () => clearTimeout(timeout)
  }, [setNow])

  return (
    <Center h="full" w="full">
      <VStack gap="md">
        <CircularProgress value={now} size="xs" />
        <Text as="small" textStyle="heading-xs">
          Loading...
        </Text>
      </VStack>
    </Center>
  )
}
Copy
'use client'

import { Box, HStack } from '@/styled-system/jsx'
import { DataGrid } from '@cerberus/data-grid'
import { Button, Show, Tag, Text } from '@cerberus/react'
import { createQuery, useQuery, useSignal } from '@cerberus/signals'
import { Suspense, useRef } from 'react'
import { createFakeQuery } from '../quick-start/data'
import { columnHelper } from '../quick-start/helper.demo'

export function ColumnDemo() {
  const data = createFakeQuery(25)
  return (
    <HStack h="20rem" w="3/4">
      <DataGrid columns={columns} data={data()} />
    </HStack>
  )
}

// primitives/example.ts

type Status = 'pending' | 'success' | 'error'

// columns.tsx

interface SmartColumnProps {
  row: string
  value: string
}

function SmartColumn(props: SmartColumnProps) {
  // 1. Local state for JUST this row
  const [_, setTrigger, getTrigger] = useSignal<Status | null>(null)

  // 2. A localized query dedicated entirely to this specific row ID
  const ref = useRef(
    createQuery(getTrigger, (currentStatus) => {
      return new Promise<Status>((resolve) => {
        setTimeout(() => resolve(currentStatus), 1000)
      })
    }),
  )

  // 3. Subscribes ONLY to this row's fetch
  const status = useQuery(ref.current)

  return (
    <HStack justify="space-between" w="full">
      <HStack gap="sm">
        <Text textStyle="body-md">{props.value}</Text>
        <Show when={status}>
          <Tag palette="success">{status}</Tag>
        </Show>
      </HStack>

      <Button onClick={() => setTrigger('success')} size="sm">
        Update
      </Button>
    </HStack>
  )
}

export const columns = [
  columnHelper.accessor('id', {
    header: 'ID',
    features: {
      pinning: {
        defaultPosition: 'left',
      },
      sort: true,
    },
    cell: ({ row, value }) => (
      <Suspense fallback={<Box aria-busy h="1/3" rounded="full" w="full" />}>
        <SmartColumn row={String(row.id)} value={value} />
      </Suspense>
    ),
  }),

  columnHelper.accessorFn((row) => `${row.firstName.slice(0, 1)} ${row.lastName}`, {
    id: 'fullName',
    header: 'Employee',
    cell: ({ value }) => <Text textStyle="body-md">{value}</Text>,
  }),
]