Duck Faq
The questions people ask before they install, plus the FAQPage JSON-LD that lets an answer engine quote them — both from one array.
BlockServer safelucide-react
Preview
Questions before the install.
Answered without the sales voice.
Does this replace shadcn/ui?
No. duck/ui is additive and rides shadcn's distribution rails. The theme ships the full CSS variable contract, so components already in the project inherit the tokens without a markup change.
Is there a runtime dependency?
No. The CLI copies source files into the repository, so there is nothing to install and nothing to remove later. Once a file lands in components/ui, it is yours to edit.
What does the theme need?
React 19, Tailwind CSS v4 and a project already configured for shadcn/ui. Install the theme before any component, because every component assumes its tokens exist.
Still stuck? Open an issue.
Installation
One command writes the section and every component it renders.
$
pnpm dlx shadcn@latest add @duck/duck-faqThe block lands in components/blocks/duck-faq.tsx. It builds on sticker-card, which the CLI installs alongside it. Already installed items are skipped.
Usage
import { DuckFaq, faqSchema } from "@/components/blocks/duck-faq"A block is a starting point, not a widget. The props exist so the example renders with real content — once the file is in your project, hard-code what never changes and delete the rest.
Props
Everything not listed here is forwarded to the root element.
| Prop | Type | Default | Description |
|---|---|---|---|
| title | React.ReactNode | - | Section heading. |
| description | React.ReactNode | - | Line under the heading. |
| items | DuckFaqItem[] | - | question, answer, and answerText for the schema when the answer is a node rather than a string. |
| columns | 1 | 2 | 2 | One column reads as a list, two as a wall. Two suits eight questions or more. |
| collapsible | boolean | false | Answers behind a native details element. They stay in the DOM open or closed, so ⌘F and a crawler still find them. |
| defaultOpen | number | - | With collapsible, the index that starts open. |
| jsonLd | boolean | true | Emit the FAQPage schema. Turn it off when the page already carries one. |
| url | string | - | Absolute URL of the page carrying the schema. It becomes the @id, which is what stops two sections claiming the same FAQPage. |
| footer | React.ReactNode | - | Under the grid: a support link, a mail-to. |
Rules
What keeps this section from turning into noise.
- One FAQPage per URL. Two on a page is a structured-data error, so a second DuckFaq takes jsonLd={false}.
- acceptedAnswer is plain text. An answer written as a node needs answerText beside it, or the item is left out of the schema rather than serialised as [object Object].
- Answers are never hidden from the document, only from the reader. That is why collapsible is native details and not a JS panel that renders on open.
- Write answers that survive being quoted alone: the claim first, the qualifier after, no pronoun that only resolves against the previous sentence.
