Skip to content

PivotViewer - Renderers

The card renderer determines how each item appears in the collection view. It returns structured text data — { title, labels?, values? } — and PivotViewer lays it out into the card. The labels and values arrays are paired positionally.

const cardRenderer = (item: Product) => ({
title: item.name,
labels: ['Price'],
values: [`$${item.price}`]
});
const taskCardRenderer = (item: Task) => ({
title: item.title,
labels: ['Priority', 'Status', 'Assignee', 'Estimate'],
values: [
`P${item.priority}`,
item.status,
item.assignee,
`${item.estimatedHours}h`
]
});

The detail renderer shows expanded information when an item is selected.

const detailRenderer = (item: Product) => (
<div className="product-details">
<h2>{item.name}</h2>
<img src={item.imageUrl} alt={item.name} className="detail-image" />
<p className="description">{item.description}</p>
<div className="specs">
<p><strong>SKU:</strong> {item.sku}</p>
<p><strong>Price:</strong> ${item.price}</p>
<p><strong>Stock:</strong> {item.stock} units</p>
</div>
</div>
);
const taskDetailRenderer = (item: Task) => (
<div className="task-details">
<div className="detail-header">
<h2>{item.title}</h2>
<div className="header-badges">
<span className={`priority-badge p-${item.priority}`}>
Priority {item.priority}
</span>
<span className={`status-badge status-${item.status}`}>
{item.status}
</span>
</div>
</div>
<div className="detail-section">
<h3>Description</h3>
<p>{item.description}</p>
</div>
<div className="detail-section">
<h3>Assignment</h3>
<div className="assignee-info">
<img src={item.assigneeAvatar} alt={item.assignee} />
<div>
<strong>{item.assignee}</strong>
<p>{item.assigneeEmail}</p>
</div>
</div>
</div>
<div className="detail-section">
<h3>Timeline</h3>
<div className="timeline">
<p><strong>Created:</strong> {formatDate(item.createdAt)}</p>
<p><strong>Due:</strong> {formatDate(item.dueDate)}</p>
<p><strong>Estimated:</strong> {item.estimatedHours} hours</p>
</div>
</div>
<div className="detail-section">
<h3>Tags</h3>
<div className="tags-list">
{item.tags.map(tag => (
<span key={tag} className="tag-pill">{tag}</span>
))}
</div>
</div>
{item.attachments && item.attachments.length > 0 && (
<div className="detail-section">
<h3>Attachments</h3>
<ul className="attachments-list">
{item.attachments.map(att => (
<li key={att.id}>
<a href={att.url}>{att.name}</a>
</li>
))}
</ul>
</div>
)}
</div>
);

Add interactive elements to the detail panel:

const interactiveDetailRenderer = (item: Task) => {
const [editing, setEditing] = useState(false);
const [notes, setNotes] = useState(item.notes);
const handleSaveNotes = async () => {
await updateTask(item.id, { notes });
setEditing(false);
};
return (
<div className="task-details-interactive">
<h2>{item.title}</h2>
{/* ... other sections ... */}
<div className="detail-section">
<div className="section-header">
<h3>Notes</h3>
<button onClick={() => setEditing(!editing)}>
{editing ? 'Cancel' : 'Edit'}
</button>
</div>
{editing ? (
<div>
<textarea
value={notes}
onChange={(e) => setNotes(e.target.value)}
rows={5}
/>
<button onClick={handleSaveNotes}>Save</button>
</div>
) : (
<p>{notes || 'No notes'}</p>
)}
</div>
<div className="detail-actions">
<button onClick={() => handleStatusChange(item.id)}>
Change Status
</button>
<button onClick={() => handleReassign(item.id)}>
Reassign
</button>
<button onClick={() => handleDelete(item.id)}>
Delete
</button>
</div>
</div>
);
};

Adapt the structured card data based on item properties:

const adaptiveCardRenderer = (item: Task) => {
const isOverdue = new Date(item.dueDate) < new Date();
const isHighPriority = item.priority >= 8;
const labels: string[] = ['Status'];
const values: string[] = [item.status];
if (isOverdue) {
labels.push('Flag');
values.push('Overdue');
}
if (isHighPriority) {
labels.push('Priority');
values.push('High');
}
return { title: item.title, labels, values };
};
  1. Keep cards compact: Display essential info only
  2. Use visual hierarchy: Most important info should stand out
  3. Consistent sizing: All cards should be similar size
  4. Show preview: Give enough context to identify items
  5. Use icons and colors: Visual cues help quick scanning
  6. Limit text: Truncate long content with ellipsis
  7. Include imagery: Photos/icons make cards more scannable
  1. Show comprehensive info: This is the place for all details
  2. Organize in sections: Group related information
  3. Make it actionable: Include relevant actions/buttons
  4. Load related data: Fetch additional details as needed
  5. Use tabs for complex data: Don’t overwhelm with single view
  6. Show relationships: Link to related items
  7. Include metadata: Timestamps, IDs, version info
  8. Consider mobile: Ensure details work on small screens