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.
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:
rowfor the active recordrowIdfor stable actions, analytics, or linksclose()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.storageKeyif 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
Spacecan toggle preview from the active rowEsccloses 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
renderPreviewcontent
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
SpaceandEscstill make sense in the overall row workflow- row click, selection, preview, and full navigation still feel distinct
Where to go next
- For the initial feature wiring, read Add row preview.
- For the broader map of customization layers, read Customization overview.
- For the lighter row-level rendering layer before preview, read Custom React cells.
- For deeper source edits to shipped surfaces, read Replacing built-in UI elements.