mdxui

DetailPanel

Side panel component for displaying read-only detail information

DetailPanel

The DetailPanel component displays read-only field values in a side panel layout. It's commonly used to show detailed information about a selected item without leaving the main view.

Type Signature

interface DetailPanelProps {
  /** Panel title */
  title?: string
  /** Panel description */
  description?: string
  /** Detail fields */
  fields: Array<{
    label: string
    value: any
    type?: string
    format?: (value: any) => string
  }>
  /** Layout arrangement */
  layout?: 'single' | 'two-column' | 'grid'
  /** Show field labels */
  showLabels?: boolean
  /** Panel className */
  className?: string
  /** Collapsible panel */
  collapsible?: boolean
  /** Start collapsed */
  defaultCollapsed?: boolean
}

Usage

<DetailPanel
  title="User Details"
  description="View user information"
  fields={[
    { label: 'Name', value: 'John Doe' },
    { label: 'Email', value: 'john@example.com' },
    { label: 'Role', value: 'Admin' },
    { label: 'Status', value: 'Active' },
    { label: 'Created', value: '2024-01-15', type: 'date' },
  ]}
/>

Props

title

The panel title displayed in the header.

<DetailPanel title="Project Details" fields={fields} />

description

Optional panel description or subtitle.

<DetailPanel
  title="API Key"
  description="View API key configuration and usage"
  fields={fields}
/>

fields

Array of field definitions to display. Each field has a label, value, optional type, and custom formatter.

<DetailPanel
  fields={[
    {
      label: 'Name',
      value: 'Acme Project',
    },
    {
      label: 'Created',
      value: '2024-01-15T10:30:00Z',
      type: 'date',
      format: (val) => new Date(val).toLocaleDateString(),
    },
    {
      label: 'Budget',
      value: 50000,
      type: 'currency',
      format: (val) => `$${val.toLocaleString()}`,
    },
    {
      label: 'Active',
      value: true,
      type: 'boolean',
    },
  ]}
/>

layout

Field layout arrangement. Defaults to 'single'.

// Single column (default)
<DetailPanel layout="single" fields={fields} />

// Two columns
<DetailPanel layout="two-column" fields={fields} />

// Grid layout
<DetailPanel layout="grid" fields={fields} />

showLabels

Controls whether field labels are displayed. Defaults to true.

<DetailPanel
  showLabels={false}
  fields={fields}
/>

collapsible

Makes the panel collapsible with expand/collapse controls.

<DetailPanel
  collapsible={true}
  defaultCollapsed={false}
  fields={fields}
/>

Common Patterns

User Profile Details

<DetailPanel
  title="Profile"
  fields={[
    { label: 'Full Name', value: user.name },
    { label: 'Email', value: user.email },
    { label: 'Phone', value: user.phone },
    { label: 'Location', value: user.location },
    { label: 'Timezone', value: user.timezone },
    { label: 'Member Since', value: user.createdAt, type: 'date' },
  ]}
/>

API Key Details

<DetailPanel
  title="API Key Details"
  description="Key configuration and usage information"
  fields={[
    { label: 'Key Name', value: apiKey.name },
    { label: 'Key ID', value: apiKey.id },
    {
      label: 'Created',
      value: apiKey.createdAt,
      format: (val) => new Date(val).toLocaleDateString(),
    },
    { label: 'Last Used', value: apiKey.lastUsedAt, type: 'date' },
    { label: 'Requests', value: apiKey.requestCount },
    { label: 'Rate Limit', value: `${apiKey.rateLimit}/hour` },
    { label: 'Status', value: apiKey.enabled ? 'Active' : 'Disabled' },
  ]}
/>

Project Information

<DetailPanel
  title="Project Info"
  layout="two-column"
  fields={[
    { label: 'Name', value: project.name },
    { label: 'Description', value: project.description },
    { label: 'Owner', value: project.owner.name },
    { label: 'Team', value: project.team.name },
    { label: 'Status', value: project.status },
    { label: 'Priority', value: project.priority },
    {
      label: 'Budget',
      value: project.budget,
      format: (val) => `$${val.toLocaleString()}`,
    },
    { label: 'Start Date', value: project.startDate, type: 'date' },
    { label: 'End Date', value: project.endDate, type: 'date' },
    {
      label: 'Progress',
      value: project.progress,
      format: (val) => `${val}%`,
    },
  ]}
/>

Transaction Details

<DetailPanel
  title="Transaction"
  fields={[
    { label: 'ID', value: transaction.id },
    { label: 'Type', value: transaction.type },
    {
      label: 'Amount',
      value: transaction.amount,
      format: (val) => `$${(val / 100).toFixed(2)}`,
    },
    { label: 'Status', value: transaction.status },
    { label: 'Customer', value: transaction.customer.name },
    { label: 'Payment Method', value: transaction.paymentMethod },
    { label: 'Date', value: transaction.createdAt, type: 'date' },
    { label: 'Invoice', value: transaction.invoiceId },
  ]}
/>

Field Types and Formatting

Built-in Types

The panel recognizes common field types:

<DetailPanel
  fields={[
    { label: 'Text', value: 'Hello World', type: 'string' },
    { label: 'Number', value: 42, type: 'number' },
    { label: 'Date', value: '2024-01-15', type: 'date' },
    { label: 'Boolean', value: true, type: 'boolean' },
    { label: 'Currency', value: 5000, type: 'currency' },
  ]}
/>

Custom Formatters

Use the format function for custom value display:

<DetailPanel
  fields={[
    {
      label: 'Duration',
      value: 3600,
      format: (seconds) => {
        const hours = Math.floor(seconds / 3600)
        const minutes = Math.floor((seconds % 3600) / 60)
        return `${hours}h ${minutes}m`
      },
    },
    {
      label: 'File Size',
      value: 1536000,
      format: (bytes) => {
        const mb = (bytes / 1024 / 1024).toFixed(2)
        return `${mb} MB`
      },
    },
    {
      label: 'Percentage',
      value: 0.7532,
      format: (val) => `${(val * 100).toFixed(2)}%`,
    },
  ]}
/>

Layout Examples

Single Column

Default layout with full-width fields:

┌──────────────────────┐
│ Name:    John Doe    │
│ Email:   john@...    │
│ Role:    Admin       │
│ Status:  Active      │
└──────────────────────┘
<DetailPanel layout="single" fields={fields} />

Two Column

Fields arranged in two columns:

┌────────────────────────────┐
│ Name:   John    Role: Admin│
│ Email:  john@...Status:    │
│         .com       Active  │
└────────────────────────────┘
<DetailPanel layout="two-column" fields={fields} />

Grid Layout

Responsive grid with multiple columns:

<DetailPanel
  layout="grid"
  fields={[
    { label: 'Field 1', value: 'Value 1' },
    { label: 'Field 2', value: 'Value 2' },
    { label: 'Field 3', value: 'Value 3' },
    { label: 'Field 4', value: 'Value 4' },
    { label: 'Field 5', value: 'Value 5' },
    { label: 'Field 6', value: 'Value 6' },
  ]}
/>

Panel Context Usage

DetailPanel can be used in different contexts:

Side Panel (Slideover)

Display details in a slideover panel:

<Shell>
  <DashboardView>
    <ItemList onSelect={(item) => setSelectedItem(item)} />
  </DashboardView>

  {selectedItem && (
    <Slideover open={!!selectedItem} onClose={() => setSelectedItem(null)}>
      <DetailPanel
        title={selectedItem.name}
        fields={[
          { label: 'ID', value: selectedItem.id },
          { label: 'Status', value: selectedItem.status },
          { label: 'Created', value: selectedItem.createdAt, type: 'date' },
        ]}
      />
    </Slideover>
  )}
</Shell>

Inline Panel

Embedded within a page layout:

<div className="grid grid-cols-3 gap-4">
  <div className="col-span-2">
    <MainContent />
  </div>
  <div className="col-span-1">
    <DetailPanel
      title="Details"
      collapsible={true}
      fields={fields}
    />
  </div>
</div>

Show details in a modal:

<Dialog open={showDetails} onClose={() => setShowDetails(false)}>
  <DetailPanel
    title="Full Details"
    layout="two-column"
    fields={fields}
  />
</Dialog>

Collapsible Panels

Make panels collapsible to save space:

<DetailPanel
  title="Additional Information"
  collapsible={true}
  defaultCollapsed={true}
  fields={[
    { label: 'Created By', value: 'John Doe' },
    { label: 'Modified By', value: 'Jane Smith' },
    { label: 'Created At', value: createdAt, type: 'date' },
    { label: 'Modified At', value: modifiedAt, type: 'date' },
  ]}
/>

Empty States

Handle cases with no data:

<DetailPanel
  title="Details"
  fields={data.length > 0 ? data : [
    { label: 'No data', value: 'Select an item to view details' }
  ]}
/>

Accessibility

DetailPanel implements accessibility best practices:

  • Semantic HTML - Uses definition lists (<dl>, <dt>, <dd>)
  • ARIA labels - Descriptive labels for screen readers
  • Keyboard navigation - Collapsible panels keyboard accessible
  • Focus management - Proper focus handling for interactive elements
<aside role="complementary" aria-labelledby="detail-panel-title">
  <h2 id="detail-panel-title">User Details</h2>
  <dl>
    <dt>Name</dt>
    <dd>John Doe</dd>
    <dt>Email</dt>
    <dd>john@example.com</dd>
  </dl>
</aside>

Best Practices

Organize fields into logical groups:

<div className="space-y-4">
  <DetailPanel
    title="Basic Information"
    fields={basicFields}
  />
  <DetailPanel
    title="Contact Details"
    fields={contactFields}
  />
  <DetailPanel
    title="Metadata"
    collapsible={true}
    defaultCollapsed={true}
    fields={metadataFields}
  />
</div>

Use Appropriate Layout

Choose layout based on field count and content:

// Few fields (< 5) - use single column
<DetailPanel layout="single" fields={shortList} />

// Many fields (5-10) - use two columns
<DetailPanel layout="two-column" fields={mediumList} />

// Lots of fields (> 10) - use grid
<DetailPanel layout="grid" fields={longList} />

Format Values Consistently

Apply consistent formatting across your app:

const formatters = {
  currency: (val) => `$${val.toLocaleString()}`,
  date: (val) => new Date(val).toLocaleDateString(),
  percentage: (val) => `${(val * 100).toFixed(1)}%`,
}

<DetailPanel
  fields={[
    { label: 'Amount', value: 5000, format: formatters.currency },
    { label: 'Date', value: date, format: formatters.date },
    { label: 'Rate', value: 0.15, format: formatters.percentage },
  ]}
/>

On this page