Repeatable group

Collapsible card group for repeating sets of fields. Supports required validation, custom titles, and a selectable mode that dedupes across instances.

Add team members with their contact details

Team Member #1
Incomplete
1 item
Installation
$Terminal
npx shadcn@latest add https://sdk-components.thesqd.com/r/repeatable-group-block.json
Usage
Define the field schema and pass controlled instance state.
TSImport
import { RepeatableGroup } from "@/components/blocks/repeatable-group-block";
import type {
  GroupInstance,
  RepeatableField,
} from "@/components/blocks/repeatable-group-block";
TSExample
import * as React from "react";
import { RepeatableGroup } from "@/components/blocks/repeatable-group-block";
import type {
  GroupInstance,
  RepeatableField,
} from "@/components/blocks/repeatable-group-block";

const fields: RepeatableField[] = [
  { key: "name",  label: "Full Name", type: "text",  required: true },
  { key: "email", label: "Email",     type: "email", required: true },
  {
    key: "role",
    label: "Role",
    type: "select",
    required: true,
    options: [
      { label: "Admin",  value: "admin"  },
      { label: "Editor", value: "editor" },
      { label: "Viewer", value: "viewer" },
    ],
  },
  { key: "primary", label: "Primary contact", type: "checkbox" },
];

export function Example() {
  const [contacts, setContacts] = React.useState<GroupInstance[]>([
    { name: "", email: "", role: "", primary: false },
  ]);

  return (
    <RepeatableGroup
      label="Team Member"
      caption="Add team members with their contact details"
      required
      fields={fields}
      minInstances={1}
      maxInstances={5}
      value={contacts}
      onChange={setContacts}
      titleField="name"
    />
  );
}
Composition
Anatomy of the repeatable group.
RepeatableGroup
├── Header (label / caption / required indicator)
├── GroupInstance[] (collapsible cards, one per repeated entry)
│   └── Fields rendered from RepeatableField[]
└── Add button (capped by maxInstances; selectable mode dedupes options across instances)
Team members
Uses titleField to show the typed name in the card header, plus controlled collapse state.

Add team members with their contact details

Team Member #1
Incomplete
1 item
Skills
Includes a multi-select chip field for categories.
Skill #1
Incomplete
1 item
Access requests
Mixes a checkbox group with a toggle.

Request access to systems with specific permissions

Access Request #1
Incomplete
1 item
Selectable mode
Each card picks from a shared list — used options are hidden from subsequent instances.

Assign team members to tasks. Each task can only be selected once.

Task #1
Incomplete
1 item
API Reference
Props for the RepeatableGroup block and its field schema.

RepeatableGroup

PropTypeDefaultDescription
labelstringHeader label.
itemLabelstringPer-card header prefix, shown as `{itemLabel} #{n}` (e.g. "Event #1"). Defaults to `label`. Overridden by `titleField` once it has a value.
captionstringSub-text under the label.
requiredbooleanfalseRenders required indicator + enforces minInstances.
fieldsRepeatableField[]Schema rendered inside each card.
valueGroupInstance[]Controlled instance array.
onChange(next: GroupInstance[]) => voidFired on every edit.
minInstancesnumber0Minimum cards.
maxInstancesnumberCap on the number of cards.
titleFieldstringField key shown in the collapsed card header.
collapsedRecord<number, boolean>Controlled collapse state by index.
onCollapsedChange(next: Record<number, boolean>) => voidCollapse change handler.
selectablebooleanfalseEach card picks from a shared option pool, deduped across instances.
selectableOptions{ label: string; value: string }[]Options for selectable mode.
selectableKeystringField key bound to the picked option.

RepeatableField

PropTypeDefaultDescription
keystringMaps to the value key inside each instance.
labelstringForm label.
captionstringHelper text under the field label.
type"text" | "email" | "phone" | "textarea" | "select" | "multiSelect" | "checkbox" | "checkboxGroup" | "toggle"Renders the matching control.
options{ label: string; value: string }[]Required for select / multiSelect / checkboxGroup.
placeholderstringInput placeholder.
requiredbooleanfalseValidates the field on submit.