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.jsonUsage
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
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Header label. |
itemLabel | string | — | Per-card header prefix, shown as `{itemLabel} #{n}` (e.g. "Event #1"). Defaults to `label`. Overridden by `titleField` once it has a value. |
caption | string | — | Sub-text under the label. |
required | boolean | false | Renders required indicator + enforces minInstances. |
fields | RepeatableField[] | — | Schema rendered inside each card. |
value | GroupInstance[] | — | Controlled instance array. |
onChange | (next: GroupInstance[]) => void | — | Fired on every edit. |
minInstances | number | 0 | Minimum cards. |
maxInstances | number | — | Cap on the number of cards. |
titleField | string | — | Field key shown in the collapsed card header. |
collapsed | Record<number, boolean> | — | Controlled collapse state by index. |
onCollapsedChange | (next: Record<number, boolean>) => void | — | Collapse change handler. |
selectable | boolean | false | Each card picks from a shared option pool, deduped across instances. |
selectableOptions | { label: string; value: string }[] | — | Options for selectable mode. |
selectableKey | string | — | Field key bound to the picked option. |
RepeatableField
| Prop | Type | Default | Description |
|---|---|---|---|
key | string | — | Maps to the value key inside each instance. |
label | string | — | Form label. |
caption | string | — | Helper 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. |
placeholder | string | — | Input placeholder. |
required | boolean | false | Validates the field on submit. |