Package Exports
- @object-ui/plugin-detail
Readme
@object-ui/plugin-detail
DetailView plugin for ObjectUI - A comprehensive detail page component with field grouping, tabs, related lists, and action buttons.
Features
- Field Grouping/Sections: Organize fields into logical sections with titles
- Collapsible Sections: Make sections collapsible to save space
- Tab Navigation: Organize content into tabs for better UX
- Related Lists: Display related records (e.g., contacts for an account)
- Action Buttons: Edit, Delete, and custom action buttons
- Readonly/Edit Mode: Toggle between view and edit modes
- Back Navigation: Built-in back button with customizable behavior
- Loading States: Skeleton loading for async data
- Custom Headers/Footers: Flexible customization options
Installation
pnpm add @object-ui/plugin-detailUsage
Basic Example
import { DetailView } from '@object-ui/plugin-detail';
function ContactDetail() {
return (
<DetailView
schema={{
type: 'detail-view',
title: 'Contact Details',
data: {
name: 'John Doe',
email: 'john@example.com',
phone: '+1234567890',
company: 'Acme Corp',
},
fields: [
{ name: 'name', label: 'Full Name' },
{ name: 'email', label: 'Email' },
{ name: 'phone', label: 'Phone' },
{ name: 'company', label: 'Company' },
],
showBack: true,
showEdit: true,
showDelete: true,
}}
/>
);
}With Sections
<DetailView
schema={{
type: 'detail-view',
title: 'Account Details',
sections: [
{
title: 'Basic Information',
icon: 'π',
fields: [
{ name: 'name', label: 'Account Name' },
{ name: 'industry', label: 'Industry' },
{ name: 'website', label: 'Website' },
],
columns: 2,
},
{
title: 'Address',
collapsible: true,
defaultCollapsed: false,
fields: [
{ name: 'street', label: 'Street' },
{ name: 'city', label: 'City' },
{ name: 'state', label: 'State' },
{ name: 'zipcode', label: 'Zip Code' },
],
columns: 2,
},
],
data: accountData,
}}
/>With Tabs and Related Lists
<DetailView
schema={{
type: 'detail-view',
title: 'Account: Acme Corp',
objectName: 'accounts',
resourceId: '12345',
fields: [
{ name: 'name', label: 'Account Name' },
{ name: 'industry', label: 'Industry' },
],
tabs: [
{
key: 'details',
label: 'Details',
icon: 'π',
content: {
type: 'detail-section',
fields: [
{ name: 'description', label: 'Description' },
{ name: 'employees', label: 'Employee Count' },
],
},
},
{
key: 'activity',
label: 'Activity',
badge: '12',
content: {
type: 'activity-timeline',
data: activityData,
},
},
],
related: [
{
title: 'Contacts',
type: 'table',
api: '/api/accounts/12345/contacts',
columns: ['name', 'email', 'phone', 'title'],
},
{
title: 'Opportunities',
type: 'table',
api: '/api/accounts/12345/opportunities',
columns: ['name', 'amount', 'stage', 'close_date'],
},
],
showEdit: true,
showDelete: true,
}}
onEdit={() => navigate('/accounts/12345/edit')}
onDelete={() => deleteAccount('12345')}
onBack={() => navigate('/accounts')}
/>Schema
The DetailView component accepts a DetailViewSchema:
interface DetailViewSchema {
type: 'detail-view';
title?: string;
objectName?: string;
resourceId?: string | number;
api?: string;
data?: any;
sections?: DetailViewSection[];
fields?: DetailViewField[];
tabs?: DetailViewTab[];
related?: RelatedList[];
actions?: ActionSchema[];
showBack?: boolean;
showEdit?: boolean;
showDelete?: boolean;
backUrl?: string;
editUrl?: string;
deleteConfirmation?: string;
loading?: boolean;
header?: SchemaNode;
footer?: SchemaNode;
}Components
DetailSection
Renders a group of fields with optional collapsing.
DetailTabs
Tab navigation for organizing content into different views.
RelatedList
Displays related records in list, grid, or table format.
Compatibility
- React: 18.x or 19.x
- Node.js: β₯ 18
- TypeScript: β₯ 5.0 (strict mode)
@objectstack/spec: ^3.3.0@objectstack/client: ^3.3.0- Tailwind CSS: β₯ 3.4 (for packages with UI)
Links
- π Documentation
- π¦ npm package
- π Changelog
- π Report an issue
- π€ Contributing Guide
- πΊοΈ Roadmap
Reference Rail decision matrix
The "Reference Rail" is the right-hand column on the record detail page
that surfaces summary cards for related collections (similar to
Salesforce's Related rail and HubSpot's About this record
sidebar). It is rendered by the record:reference_rail component and
emits automatically when:
- The page is generated by the synth (
buildDefaultPageSchema) β i.e. no explicitPageoverrides the object's detail view. - The objectDef declares β₯2 related collections (lookup/master-detail inbound fields).
- The viewport is β₯ xl (1280 px) β below that the rail collapses and the Related tab keeps full coverage.
- The objectDef does not opt out via
detail.hideReferenceRail.
When the rail emits, the synth automatically suppresses the Related tab so the same information isn't shown twice.
Per-object opt-out
Add a detail block to the objectDef:
ObjectSchema.create({
name: 'product',
// β¦
detail: {
hideReferenceRail: true, // hide the rail; restore the Related tab
hideRelatedTab: true, // (optional) force-hide the Related tab too
},
});CRM business-domain guidance
| Object type | Rail | Why |
|---|---|---|
| Hub objects | on | Account / Opportunity / Contact / Case β users browse laterally to quotes, contacts, activities |
| Transactional | on | Quote / Contract / Order β show line-items + related parties at a glance |
| Campaign / Event | on | Members, responses, child campaigns |
| Catalog | off | Product / Price Book β users edit attributes; lateral relationships are noise |
| Atomic action | off | Task / Note β focused single-column edit beats a related-list rail |
| Lead (unconverted) | off | Pre-conversion records have no children β keep it focused on the form |
Adding the rail to a custom Page
For explicit (non-synth) Pages, add an aside region after the main
region:
{
name: 'aside',
width: 'small',
className: 'hidden xl:flex flex-col gap-4',
components: [
{
type: 'record:reference_rail',
id: 'opp_reference_rail',
properties: {
entries: [
{ objectName: 'quote', relationshipField: 'opportunity', title: 'Quotes', limit: 3 },
{ objectName: 'opportunity_line_item', relationshipField: 'opportunity', title: 'Products', limit: 3 },
{ objectName: 'task', relationshipField: 'related_to_opportunity', title: 'Open Tasks', limit: 3 },
],
},
},
],
},The renderer reads entries from both schema.entries and
schema.properties.entries so either spec-style or flat authoring works.
License
MIT β see LICENSE.