Brik Design System
Components

Addable entry list

Title + description sibling. Each row carries a primary value and a secondary note.

AddableEntryList is the text + textarea sibling of AddableTextList. Use for lists where each row needs a headline value plus context — competitor URL + notes, reference site + why, line item + description, team member + role.

The entry shape is intentionally generic ({ primary, secondary }) so a single component serves all of these. Map your domain shape at the boundary.

Use it for

  • Competitor list (URL + notes)
  • Reference sites (URL + why we like it)
  • Service catalog entries (name + description)
  • Line items (label + amount/description)
  • Team member rosters (name + role)

For free-form one-string-per-row, use AddableTextList. For ≥3 structured fields, use AddableFieldRowList.

Import

import { AddableEntryList } from '@brikdesigns/bds';

Modes

AddableEntryList has three rendering modes driven by props:

Plain mode

No primarySuggestions. Each entry is inline-editable: a primary input + secondary textarea + Remove button per row. The Add button appends a new empty row. Default and the right choice for free-form lists (competitors, reference sites, line items).

import { useState } from 'react';

const [entries, setEntries] = useState<{ primary: string; secondary: string }[]>([]);

<AddableEntryList
  label="Competitors"
  primaryLabel="URL"
  secondaryLabel="Notes"
  primaryInputType="url"
  primaryPlaceholder="https://"
  secondaryPlaceholder="What stands out?"
  entries={entries}
  onChange={setEntries}
/>

Suggestion mode

With primarySuggestions. Preserves the reveal-form flow — existing entries render as read-only cards with an x-icon remove. Add reveals a staging combobox. Use for vocabulary-locked lists (services from a catalog).

const services = getIndustryServices(company.industry_slug);

<AddableEntryList
  label="Services offered"
  primaryLabel="Service"
  secondaryLabel="What makes this unique here?"
  primarySuggestions={services}
  entries={entries}
  onChange={setEntries}
/>

With primaryStrict, free-form entries outside the suggestion list are rejected. For full BCS-aware multi-pick with source: 'catalog' | 'custom' attribution, use CatalogPicker — it wraps AddableEntryList with extra metadata.

Read mode

disabled={true} collapses both modes to token-backed typography: primary uses --label-md, secondary uses --body-md. When primaryInputType="url", the primary renders as a clickable anchor.

<AddableEntryList
  entries={entries}
  onChange={() => {}}
  primaryInputType="url"
  emptyDescriptionLabel="No description"
  disabled
/>

Primary input type

ValueBehaviorRead-mode render
'text' (default)Plain text inputStatic text
'url'type="url" + URL autocompleteClickable anchor with brand color + underline

Empty description fallback

When emptyDescriptionLabel is set, read-mode rows with empty secondary show that fallback text instead of collapsing the slot. Useful when consistent row heights matter.

<AddableEntryList
  entries={entries}
  onChange={() => {}}
  emptyDescriptionLabel="No description set"
  disabled
/>

Behavior

Plain mode

  • Add appends an empty { primary: '', secondary: '' } row.
  • Typing into any field patches that row's primary or secondary via onChange.
  • Remove drops the row immediately.
  • When maxItems is reached, the Add button hides.

Suggestion mode

  • Add reveals a staging form with a combobox-backed primary (filtered by typed query).
  • Arrow + Enter commits a highlighted suggestion, moves focus to the secondary textarea.
  • Save commits the staged entry and keeps the form open for rapid entry.
  • Esc cancels the staging form.
  • Duplicates blocked case-insensitively unless allowDuplicates.
  • With primaryStrict, free-form entries outside primarySuggestions are rejected.

When not to use

For free-form one-string-per-row, use AddableTextList. AddableEntryList's textarea is wasted vertical space when there's no description to enter. AddableTextList console-warns when values contain , , :, newline, or tab to catch mis-pickings at the source.

  • Don't use for ≥3 structured fields. Use AddableFieldRowList.
  • Don't use for locked vocabularies with provenance tracking. Use CatalogPicker — it wraps this component with source: 'catalog' | 'custom' and stable slug derivation.

Accessibility

  • Plain mode: each row is a labeled fieldset with two real inputs. Tab order moves through primary → secondary → remove.
  • Suggestion mode: combobox + textarea, both with proper ARIA. Read-only cards have aria-label on the remove button.
  • Read mode: primary anchors get target / rel defaults preserving security on external URLs.

API

PropTypeDefault
entriesAddableEntry[] (required)
onChange(next: AddableEntry[]) => void (required)
labelstring
helperTextstring
primaryLabelstring
secondaryLabelstring
primaryPlaceholderstring
secondaryPlaceholderstring
primaryInputType'text' | 'url''text'
primarySuggestionsstring[]
primaryStrictbooleanfalse
addLabelstring
removeLabelstring
emptyLabelstring
emptyDescriptionLabelstring
size'sm' | 'md' | 'lg''md'
disabledbooleanfalse
maxItemsnumberunlimited
secondaryRowsnumber2
allowDuplicatesbooleanfalse

AddableEntry shape

interface AddableEntry {
  primary: string;
  secondary: string;
}

On this page