A grid of selectable image tiles. Single-select — the chosen tile gets a checkmark badge and a ring border. Supports disabled, error, and configurable column count.
*
Choose your preferred layout
Installation
$Terminal
npx shadcn@latest add https://sdk-components.thesqd.com/r/image-picker.jsonUsage
Pass an items array and a controlled value/onChange pair.
TSImport
import { ImagePicker, type ImagePickerItem } from "@/components/blocks/image-picker";TSExample
"use client";
import { useState } from "react";
import { ImagePicker, type ImagePickerItem } from "@/components/blocks/image-picker";
const items: ImagePickerItem[] = [
{ id: "a", src: "/images/layout-a.jpg", alt: "Layout A", caption: "Layout A" },
{ id: "b", src: "/images/layout-b.jpg", alt: "Layout B", caption: "Layout B" },
{ id: "c", src: "/images/layout-c.jpg", alt: "Layout C", caption: "Layout C" },
];
export function Example() {
const [value, setValue] = useState("");
return (
<ImagePicker
label="Pick a layout"
description="Choose your preferred layout"
required
columns={3}
items={items}
value={value}
onChange={setValue}
/>
);
}Composition
Single component with label, grid, and error slot.
ImagePicker (single component — prop-driven grid of image tiles)
├── label / description / required (field header)
├── columns (grid column count, default 3)
├── items (ImagePickerItem[]: id + src + alt + optional caption)
├── value / onChange (controlled selection — value is the selected item id)
└── error (red border + red error text below the grid)
Each tile is a <button role="radio"> with a checkmark badge when selected.Default
Three-column grid with label, description, and required marker.
*
Choose your preferred layout
Two columns
Pass `columns={2}` for a wider tile layout — good for portrait vs landscape orientation pickers.
With description
Add a `description` to each item to show a helper line below the caption — useful when the caption alone isn't enough context.
Each option includes a brief explainer.
Error state
Pass an `error` string to show a red message below the grid and highlight unselected tiles.
*
Please select a template to continue.
Multi-select
Pass `multiple` to let users pick more than one tile. Each tile gets a `+` badge in the top-right that flips to a checkmark when selected — same pattern as the Multi-select cards radio variant.
Tap the + on any tile to add it. Tap again to remove.
Multi-select with dictation notes
Pass `withNotes` (alongside `multiple`) to reveal a textarea with browser dictation under every selected tile, so users can speak what they like about each one.
Pick the layouts you like and dictate why — tap the mic in the textarea.
Markdown content tiles
Omit `src` and pass a `content` markdown string on each item to render long-form text instead of an image. Supports paragraphs, **bold**, *italic*, lists, and inline `code`. Pairs well with `columns={1}` for a stacked layout.
*
Markdown content tiles — multi-select
Same long-form markdown tiles, but with `multiple` so users can pick more than one. The `+` badge flips to a checkmark when selected.
*
Tap the + on any option to add it. Tap again to remove.
API Reference
Props for ImagePicker and ImagePickerItem.
ImagePicker
| Prop | Type | Default | Description |
|---|---|---|---|
items | ImagePickerItem[] | — | Array of image tiles to display. |
multiple | boolean | false | Allow multiple tiles to be selected. When true, `value` is `string[]`. |
value | string | string[] | — | Controlled selection — string when single, string[] when `multiple`. |
onChange | (value: string | string[]) => void | — | Fires with the next selection. |
withNotes | boolean | false | When a tile is selected, reveal a textarea (with browser dictation) for the user to describe what they like. Best with `multiple`. |
notes | Record<string, string> | — | Controlled notes map keyed by item id. |
onNotesChange | (notes: Record<string, string>) => void | — | Fires when any selected tile's note changes. |
notesPlaceholder | string | "What do you like about this image?" | Placeholder for the per-tile notes textarea. |
columns | number | 3 | Number of columns in the grid. |
label | string | — | Field label rendered above the grid. |
description | string | — | Helper text below the label. |
required | boolean | — | Adds a red asterisk to the label. |
disabled | boolean | — | Grays out the grid and prevents interaction. |
error | string | — | Error message shown below the grid. |
ImagePickerItem
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | — | Unique identifier — used as the value when selected. |
src | string | — | Image URL. Omit when passing `content` to render text/markdown instead. |
alt | string | — | Alt text for the image. |
caption | string | — | Optional caption rendered below the image inside the tile. |
description | string | — | Optional helper text rendered below the caption — shows on each tile. |
content | string | — | Markdown string rendered in place of the image — supports paragraphs, **bold**, *italic*, lists, and inline `code`. Pairs well with `columns={1}` for stacked, text-heavy tiles. |