data · Normal DOM + Twenty Remote DOM

LineItemCard

One row of a priced line — header / body / footer slots + optional delete.

The shell of a deal item, invoice line, subscription row, project task with cost — anywhere the panel shows a repeating priced row. Owns only the chrome: outer border, accent rail, dividers between adjacent slots, and the outside delete button. Caller fills the slots.

Install

Pull from the workspace packages (already available if you ran pnpm add):

ts
import { LineItemCard } from '@8maverik8/twenty-design';

Examples

Catalog row — input + chip header opens picker modal

The deal-items archetype. Header is an InputShell with EntityChip inside; clicking anywhere on it opens a SearchPicker modal of products (live demo). Body holds only what changes PER LINE — the Annual/Monthly toggle and quantity. Deal-level params (months, start/end dates) live above the panel header, not on individual cards. Drag the bottom-right corner of the dashed wrapper to resize and watch the card adapt.

Drag the bottom-right corner ↘ to resize the panel — the card adapts to its container.
Creomate · Bundle (WebAPI + Webhook) · 32 SC
Pricing
Annual
Monthly
Quantity
Unit Price€91.50/yr
Line Total€91.50
×
tsx
<LineItemCard
onDelete={removeLine}
header={
<InputShell interactive onClick={() => setPickerOpen(true)}>
{product ? <EntityChip name={product.name} /> : <span>Select product…</span>}
</InputShell>
}
body={
<>
<SegmentedControl options={[{ value: 'annual', label: 'Annual' },]} … />
<MetaItem label="Quantity"><NumberInput … /></MetaItem>
</>
}
footer={
<>
<MetaItem label="Unit Price">€91.50/yr</MetaItem>
<MetaItem label="Line Total" strong>€91.50</MetaItem>
</>
}
/>
<Modal open={pickerOpen} title="Replace product" size="lg">
<SearchPicker query={query} onQueryChange={setQuery}>
{filtered.map((p) => <ListRow … onClick={() => pick(p)} />)}
</SearchPicker>
</Modal>

Custom row — editable name input

Same chrome — no accent rail. Catalog vs Custom row kinds differ only by header content type: catalog uses InputShell+EntityChip with a click-to-open-picker, custom uses InputShell+native input for free-text typing. Unit price is a MoneyInput. Wrapped in the same resizable container so width adaptation is visible.

Drag the bottom-right corner ↘ to resize the panel — the card adapts to its container.
Unit Price
Quantity
Line Total€314.00
×
tsx
<LineItemCard
onDelete={removeLine}
header={
<InputShell>
<input value={name} onChange={} placeholder="Custom line description…" />
</InputShell>
}
body={
<>
<MetaItem label="Unit Price"><MoneyInput valueMicros={} currency="EUR" /></MetaItem>
<MetaItem label="Quantity"><NumberInput … /></MetaItem>
</>
}
footer={<MetaItem label="Line Total" strong>€314.00</MetaItem>}
/>

Variants

accent
defaultdefaultNo coloured rail — neutral line.
primaryBlue rail — typical for "custom" rows.
successGreen rail — paid / approved lines.
warningOrange rail — needs attention.
dangerRed rail — overdue / refused.

Anatomy

Accent rail3px coloured left border to distinguish row kinds.
Header slotTop region. Typically the entity selector or free-text name input.
Body slotMiddle region. Parameter controls (qty, dates, switches).
Footer slotBottom region. Right-aligned by default — totals.
Delete buttonOutside the card border on the right. 28px wide, height-stretched.

Guidelines

Keep the same set of slots filled across all rows in a list.Visual rhythm — alternating shapes look broken.
Stuff multiple inputs in a single slot when they belong in different rows.The footer is for read-only totals; controls go in the body.