react-datatable
Customization

Customizing preview UI

Use this page when basic row preview already works, but the panel surface and interaction model still need to match your product.

Preview customization is about making inspection feel intentional: light when it should stay in-table, heavier only when the workflow really needs it.

Media placeholder: Preview panel states, placement, and close affordance

Show one realistic table with the floating preview open, the close button visible, and a second annotation calling out the panel repositioned to avoid covering important columns.

1. Start by deciding what preview should do in your product

Preview is for inspection in place.

That means a good preview usually helps users:

  • check row details without leaving the grid
  • compare nearby rows quickly
  • confirm context before opening a full record

If the design really wants editing, multi-step actions, or route-level context, that is usually an onOpenRow job instead of a preview customization job.

2. Customize preview content through renderPreview

The first customization seam is the preview body itself.

<Datatable
  tableKey="customers"
  data={customers}
  columns={columns}
  getRowId={(row) => row.id}
  preview={{
    renderPreview: ({ row, rowId, close }) => (
      <CustomerPreviewCard customer={row} rowId={rowId} onClose={close} />
    ),
  }}
/>

renderPreview receives:

  • row for the active record
  • rowId for stable actions, analytics, or links
  • close() so your preview content can dismiss the panel directly

This is the right place for product-specific summary UI, not for rewriting how preview state itself is managed.

3. Keep preview content denser than a full detail page

The built-in surface is a floating panel with a bounded width and height plus internal scrolling.

That makes it a strong fit for:

  • summary fields
  • owner, status, dates, and metadata
  • a small cluster of focused actions
  • short related context that helps the user decide what to do next

It is a weak fit for:

  • long forms
  • multi-step editing flows
  • very wide comparison layouts
  • content that only makes sense as a whole routed screen

A preview should feel like a fast inspection layer, not a cramped copy of the detail page.

4. Decide whether the floating panel should be draggable

FloatingPreview.tsx enables dragging by default.

<Datatable
  tableKey="customers"
  data={customers}
  columns={columns}
  preview={{
    floating: {
      draggable: true,
      storageKey: "customers-preview",
    },
    renderPreview: ({ row }) => <CustomerPreviewCard customer={row} />,
  }}
/>

Use draggable preview when the panel may otherwise cover important table columns and users benefit from moving it out of the way.

Set draggable: false when:

  • the table sits in a tightly controlled layout
  • consistency matters more than user-controlled placement
  • dragging would compete with another interaction model on the page

5. Use storageKey deliberately

The floating preview position is stored in sessionStorage when a stable key exists.

The current implementation uses:

  • preview.floating.storageKey if you provide one
  • otherwise the top-level tableKey

That is a useful default, but it is still a product decision.

Give the preview its own storageKey when the panel position should stay stable across visits to the same table workflow. Skip that persistence when preview placement is not important enough to preserve.

6. Preview open/close state is transient

The panel position can persist. The fact that a row is currently open in preview should not be treated like saved table state.

That boundary matters.

Preview visibility reflects what the user is inspecting right now. It is not part of a durable layout contract like filters, sorting, or views.

If a product wants deep-linkable inspection state, that is usually a route or URL-state design question, not ordinary preview customization.

7. Customize preview behavior without blurring it with row navigation

The grid already treats preview as part of row interaction:

  • clicking a data row can open preview when preview is configured
  • Space can toggle preview from the active row
  • Esc closes preview before clearing broader interaction state
  • keyboard movement can carry preview along with the active row

These defaults are useful, but they need to fit the product workflow.

If users cannot tell whether a row click means preview, selection, or navigation, the product needs a clearer interaction contract before more UI polish will help.

8. Use onTogglePreviewRow for product side effects, not the preview body

If the product needs analytics or side effects around preview activity, use rowActions.onTogglePreviewRow.

<Datatable
  tableKey="customers"
  data={customers}
  columns={columns}
  rowActions={{
    onTogglePreviewRow: ({ rowId, nextOpen }) => {
      trackPreviewToggle(rowId, nextOpen)
    },
  }}
  preview={{
    renderPreview: ({ row }) => <CustomerPreviewCard customer={row} />,
  }}
/>

This keeps instrumentation and workflow hooks at the row-action boundary instead of burying them inside the preview content component.

9. Source-level preview customization starts with FloatingPreview.tsx

When prop-level customization is not enough, the narrow source seam is features/preview/FloatingPreview.tsx.

That is the right place to change things like:

  • the panel chrome
  • close affordance styling
  • header layout
  • container sizing
  • drag-handle treatment
  • the outer shell around your renderPreview content

Be more careful when changing:

  • viewport clamping
  • persistence behavior
  • drag math
  • open/close semantics
  • keyboard expectations tied to preview state

Those behaviors shape how the table feels, not just how the panel looks.

10. Review preview in the real table context

Before you call preview customization done, check that:

  • the content helps inspection without turning into a mini app
  • the panel does not cover critical table data in a frustrating way
  • dragging and stored position behave predictably when enabled
  • the close affordance is obvious and easy to reach
  • Space and Esc still make sense in the overall row workflow
  • row click, selection, preview, and full navigation still feel distinct

Where to go next

On this page