Searchable card grid for picking a project's primary audience, with an add button that creates a new audience in a drawer or modal.
*
Please select the most appropriate category below or create a new one for your design.
onChange valuenone
Installation
$Terminal
npx shadcn add https://raw.githubusercontent.com/sis-thesqd/squad-sdk/main/public/r/audience-selection-block.jsonUsage
Drop in the demo component or wire up the picker with your own state.
TSImport
import { AudienceSelection, type Audience } from "@/components/blocks/audience-selection-block";TSExample
"use client";
import * as React from "react";
import {
AudienceSelection,
type Audience,
} from "@/components/blocks/audience-selection-block";
const AUDIENCES: Audience[] = [
{ id: "a-teens", name: "The Teens", category: "Teenagers" },
{ id: "a-kids", name: "Kids", category: "Kid-Friendly" },
{ id: "a-women", name: "Women's Ministry", category: "Women Only" },
{ id: "a-young", name: "Young Adults", category: "General Audience" },
];
export default function Page() {
// `audiences` is the list to choose from. Creating one via the add button
// appends to an internal copy (and fires `onCreate` if provided), so the
// picker is self-contained without a backing store.
//
// `value` + `onChange` control the selected audience id; omit both to let
// the picker manage its own selection.
//
// Flat props: `title`, `caption`, `required`, `showAddButton`,
// `searchEnabled`, `cols` (number | "auto"), `addLayout`
// ("drawer" | "modal"), `categories`, `addButtonLabel`, `onCreate`,
// and `error`.
const [value, setValue] = React.useState<string>();
return (
<AudienceSelection
title="Who will be the primary audience for this project?"
caption="Please select the most appropriate category below or create a new one."
required
audiences={AUDIENCES}
cols={2}
addLayout="drawer"
value={value}
onChange={setValue}
/>
);
}Composition
Anatomy of the audience selection block.
AudienceSelection (flat block)
├── FieldLabel (title + caption + required asterisk)
├── Toolbar row: Search Input (searchEnabled) + add icon-button (showAddButton)
├── RadioGroup type="card" (cols | "auto") — 3 rows, then Pagination
│ └── per audience: category icon + name (label) + category Badge + ⋯ menu (editable)
│ └── DropdownMenu → Edit (reuses the form) / Delete (destructive)
├── Create / edit form (addLayout="drawer" | "modal")
│ ├── TextField — audience name (≥2 chars, de-duped)
│ └── RadioGroup type="chip" — category
└── Delete confirmation Dialog (type="confirm")Default
A searchable grid of audience cards (category icon + name + category badge) backed by the SDK `RadioGroup`, capped at 3 rows with pagination. `title` / `caption` render the field label via `FieldLabel`; the add button opens a create form in a side drawer; and each card's ⋯ menu offers Edit (reuses the same form) and Delete (confirmation dialog). Selecting drives a controlled audience id via `value` / `onChange`.
*
Please select the most appropriate category below or create a new one for your design.
onChange valuenone
Add in a modal
Set `addLayout="modal"` to surface the create form in a centered Dialog instead of the default side Drawer. The form (name + category) is identical either way.
The “Add” button opens the create form in a centered modal.
Auto columns
`cols="auto"` wraps the cards responsively (auto-fill, min ~16rem each) instead of the default fixed 2-column grid. Pass any number to `cols` for a fixed count.
cols="auto" wraps cards responsively instead of a fixed column count.
No add button / search off
Turn off the search field with `searchEnabled={false}` and hide the create button with `showAddButton={false}` for a compact, fixed list of choices.
Search and the add button are turned off for a read-only-ish picker.