# Beste UI

> Beautiful, accessible blocks, pieces and components for shadcn/ui and Tailwind CSS, built for React and Next.js. Install one with the shadcn CLI and the code is yours to edit: no runtime dependency, nothing to upgrade.

Markdown rendition of https://ui.beste.co/blog/16-shadcn-questionnaire-blocks. Every page on the site has one: add `.md` to any address, or send `Accept: text/markdown`.

Last updated: 2026-08-07

Whole catalog for LLMs: https://ui.beste.co/llms.txt. Registry index: https://ui.beste.co/r/registry.json

---
[All posts](/blog)

August 7, 2026·11 min read

# 16 shadcn Questionnaire Blocks: Ask One Question at a Time and Answer With Something Useful

Sixteen free blocks built on the shadcn questionnaire primitive: support routers, product finders, intake forms, configurators, and shortlists that narrow as you answer. Each installs with one CLI command and turns a long form into a short conversation.

questionnaireformsshadcnreactux

![zieg](/_next/image?url=%2Fassets%2Fimages%2Fzieg.jpg&w=96&q=75&dpl=dpl_CY2dAqud5esqh3XyqpW5XSreDKej)

ziegbuilding beste.co

[zieg's website](https://zieg.beste.co)[zieg on X](https://x.com/forwardset)[zieg on LinkedIn](https://linkedin.com/in/ziegfiroyt)

Most interfaces ask badly. A support page offers a search box and hopes you know the vocabulary. A product page offers twelve filters and hopes you know your own requirements. A contact form asks fourteen questions at once, including the four that do not apply to you, and the answer arrives three days later from somebody who read none of them.

The alternative is older than the web: ask one question, listen to the answer, then ask the next one. That is what the [shadcn questionnaire primitive](https://ui.shadcn.com/docs/components/questionnaire) makes practical in a React app, and it is what these sixteen blocks are built on. Every one of them asks a short sequence, and every one ends with something concrete: a written answer, a matched product, a booked appointment type, a specification with a price on it.

All sixteen are free.

Free block

This block is free. No license or account is required: install it with the CLI and use it in unlimited projects.

### What installs

Each block is a single React component, and the CLI resolves its whole tree in one command:

bashCopy

```
npx shadcn add "https://ui.beste.co/r/faq92"
```

That lands three layers:

1. **The section** at `components/beste/block/faq92.tsx`. Plain TSX you own.
2. **The `questionnaire` primitive** at `components/ui/questionnaire.tsx`, plus whatever else the block composes.
3. **Nothing else.** No form engine, no flow builder, no runtime dependency on us.

If you are on Base UI rather than Radix, swap `/r/` for `/r-base/` and you get the [Base UI build](/blog/base-ui-the-new-default) of the same component.

### The primitive underneath

`components/ui/questionnaire.tsx` is a thin styled layer over [@shadcn/react](https://ui.shadcn.com/docs/components/questionnaire), the headless package shadcn publishes for it. It is worth understanding before you edit a block, because it does more than hide and show fields.

The parts compose like any other shadcn component:

tsxCopy

```
<Questionnaire defaultItem="area" shortcuts="numbers" onSubmit={handleSubmit}>
  <QuestionnaireProgress />

  <QuestionnaireItem name="area" required>
    <QuestionnaireTitle>What is this about?</QuestionnaireTitle>
    <QuestionnaireChoices>
      <QuestionnaireChoice value="billing">Billing and invoices</QuestionnaireChoice>
      <QuestionnaireChoice value="access">Getting into the account</QuestionnaireChoice>
    </QuestionnaireChoices>
    <QuestionnaireError />
  </QuestionnaireItem>

  <QuestionnaireActions>
    <QuestionnairePrevious />
    <QuestionnaireNext />
    <QuestionnaireSubmit />
  </QuestionnaireActions>
</Questionnaire>
```

Four things it gives you for free are the reason these blocks are short:

* **It is a real form.** Every question is a `fieldset` with a name, and the answers are ordinary form values. The blocks read them with `new FormData(event.currentTarget)` on submit, so there is no state library and nothing to serialize.
* **One item is on screen, the rest stay mounted.** Inactive questions are and inert rather than unmounted, so answers given earlier are still submitted with the rest, and the browser still owns validation.
* **Navigation understands the question.** `required` blocks Next until the question is answered, `Skip` only appears when a question is optional, and each item reports a status of `unanswered`, `answered`, or `skipped` through `onStatusChange`, which is how a block can draw a rail of progress beside the flow.
* **Keyboard shortcuts are scoped.** Set `shortcuts="letters"` or `shortcuts="numbers"` and each choice on the active question gets a key, rebound as you move. Pressing `2` means something different one question later, which is exactly what you want.

Let the primitive own navigation

Every block here keeps the flow uncontrolled and only watches it, usually with `onItemChange`, to draw a progress bar or swap a photograph. Fighting the primitive for control of the current step is the fastest way to end up with a flow that disagrees with its own buttons.

### Four things a questionnaire can end with

The sixteen blocks are not sixteen variations on a wizard. They are grouped by what happens when the answers land, because that is the decision you are actually making when you pick one.

#### One: route to a written answer

The cheapest support win is not a better search box. It is admitting that most tickets are a dozen questions wearing different words, and routing to the one that fits.

Answer Router

[View block](/block/faq92)

Loading preview…

faq92: two questions instead of a search box. The answers pick one written answer from a list of routes, and the assistant card beside them answers the question you arrived by.

`faq92` matches on a small object per answer, so a narrow route sits above a general one for the same subject and wins when both fit. The last entry is the catch-all, which means a submit never dead-ends. The same shape drives [booking29](/block/booking29), where the routes are appointment types with a length, a clinician, and a set of facts, and [careers56](/block/careers56), where they are open roles with a published pay band.

[fitness40](/block/fitness40) is the honest version of the pattern. Its result answers in two columns of equal weight: who the programme is for beside who should not take it. A recommendation that never says no is a sales page, and readers can tell.

#### Two: score the answers and recommend

When the answer is a ranking rather than a lookup, the blocks score instead of match.

Product Finder Quiz

[View block](/block/ecommerce46)

Loading preview…

ecommerce46: a product finder that replaces the filter sidebar. Each answer asks for traits, each product scores one point per trait it carries, and the winner takes over the plate with the reasons it came out on top.

The scoring is deliberately blunt: one point per trait, highest total wins, ties go to the first item. What makes it feel considered is the reasons list, which is not written by hand. It collects the reason attached to every answer the winning product actually satisfies, so the same bag explains itself differently depending on how you reached it.

[podcast54](/block/podcast54) scores the same way but returns an order rather than an item: three episodes to listen to in sequence, with the total runtime summed from the episodes themselves. [workflow55](/block/workflow55) weights each answer with a number, turns the total into a percentage of the ceiling, and places you on a scale between shipping straight away and full change control. [education102](/block/education102) grades against an answer key and shows its working, including the explanation for anything you got wrong.

#### Three: build something while you answer

The most useful thing a questionnaire can do is show its work as it goes, so nothing arrives as a surprise at the end.

Live Made To Order Configurator

[View block](/block/product12)

Loading preview…

product12: a made-to-order configurator. Every option takes over the photograph, writes its own line into a running specification, and moves a total that is visible from the first question.

`product12` never hides the price. The specification sits under the questions the whole time, filling one row per answered step, with the total recomputed from a base plus each option. [event101](/block/event101) does the same for a day at a conference: a fixed rail of time slots that fill in as you choose, with the hours you left open still on the page, because an empty afternoon is a decision worth seeing.

Narrowing Destination Shortlist

[View block](/block/travel42)

Loading preview…

travel42: a shortlist that eliminates in the open. Six photographs sit beside the questions and dim as each answer rules them out, with a live count underneath.

`travel42` inverts the filter sidebar. Instead of a result count quietly changing somewhere, you watch the grid narrow one answer at a time, which tells you what each question is costing you while you can still change it. It is possible to answer your way down to nothing, so the empty state is written copy rather than a blank panel.

[gallery25](/block/gallery25) takes the idea somewhere else again: its questions are photographs rather than sentences. You pick the frames you would hang, and the result is a distribution rather than a winner, because a split eye should read as split.

#### Four: collect, then confirm

The last group is the one closest to a traditional form, and the one where a questionnaire earns the most.

Studio Brief With Changing Plate

[View block](/block/agency24)

Loading preview…

agency24: a four-step studio brief where the photograph changes with the question on screen, and sending it swaps the form for a ledger of every answer.

A brief asked one question at a time is answered more honestly than the same brief as a wall of fields, and the closing ledger matters as much as the questions: it shows the sender exactly what is about to be sent. [onboarding43](/block/onboarding43) applies that to workspace setup, [cta92](/block/cta92) to a newsletter that asks which subjects you want before it asks for your address, and [saas108](/block/saas108) to a sales enquiry that branches, so nobody is asked about compliance for a blog.

### Every value on the screen is a prop

The rule behind the whole catalog holds here: **if you can read it, you can pass it.**

Each block renders its full demo with zero props, so `<Faq92 />` looks exactly like the embed above. Every prop is optional and falls back to the demo value, so you replace demo copy with real content one field at a time:

tsxCopy

```
import { Faq92 } from "@/components/beste/block/faq92";

export default function HelpPage() {
  return (
    <Faq92
      heading="Two questions instead of a search box"
      questions={[
        {
          name: "area",
          title: "What is this about?",
          choices: [
            { value: "billing", label: "Billing and invoices" },
            { value: "access", label: "Getting into the account" },
          ],
        },
        {
          name: "state",
          title: "Where did it stop working?",
          choices: [
            { value: "before", label: "Before I could start" },
            { value: "after", label: "It finished, but the result is wrong" },
          ],
        },
      ]}
      answers={[
        {
          match: { area: "billing", state: "after" },
          title: "The invoice does not match what you expected",
          description: "Mid-cycle plan changes are prorated to the day.",
          link: { label: "Read how proration is calculated", href: "/docs/billing" },
        },
        {
          title: "Something else",
          description: "Start from the index and narrow down from there.",
          link: { label: "Open the help index", href: "/docs" },
        },
      ]}
    />
  );
}
```

Every block exports its demo object too (`faq92Demo` here), which doubles as documentation: the exact shape your data needs, in a file you can open.

Wiring the result to your backend is the part we deliberately left to you. Each block resolves its outcome in one handler, so you add a `fetch` there and nothing else changes.

### The details we had to get right

* **Server rendering is safe.** No `Date.now()`, `Math.random()`, or `toLocaleString()` in render paths. Money and durations are formatted by hand, so a server and a client never disagree about a comma.
* **The flows are real forms.** Answers are form values, validation is the browser's, and a submit is a submit. If JavaScript never arrives, the questions are still there.
* **Keyboards work.** Choices are labelled inputs, the whole tile is the target, and shortcuts are bound only while their question is active.
* **Theming is token-based.** Everything reads from your CSS variables, so the blocks adopt your brand and your dark mode on arrival. If you are still shaping those tokens, [the anatomy of a shadcn theme](/blog/anatomy-of-a-shadcn-theme) covers the variables underneath.
* **Three design languages, one mechanic.** The sixteen are split across our Sirius, Auralis, and Polaris languages, so you can pick the one that matches the site you already have rather than restyling a stranger.

### The full set

Every block links to a live, full-screen preview. They are gathered under one hub at </blocks/tag/questionnaire>.

| Block                               | What it does                   | Ends with                           |
| ----------------------------------- | ------------------------------ | ----------------------------------- |
| [faq92](/block/faq92)               | Support router, two questions  | One written answer                  |
| [booking29](/block/booking29)       | Appointment intake             | A matched appointment type          |
| [careers56](/block/careers56)       | Role fit finder                | One open role and its pay band      |
| [fitness40](/block/fitness40)       | Programme match                | Who it is for, beside who it is not |
| [ecommerce46](/block/ecommerce46)   | Product finder                 | One product and why it won          |
| [podcast54](/block/podcast54)       | Listening path builder         | Three episodes in order             |
| [workflow55](/block/workflow55)     | Process sizer                  | A position on a weighted scale      |
| [education102](/block/education102) | Graded lesson check            | A score and a per-question review   |
| [product12](/block/product12)       | Made-to-order configurator     | A live specification and total      |
| [event101](/block/event101)         | Conference day builder         | A filled schedule, gaps included    |
| [travel42](/block/travel42)         | Narrowing shortlist            | Whatever survived the answers       |
| [gallery25](/block/gallery25)       | Taste profile from photographs | A distribution across styles        |
| [agency24](/block/agency24)         | Studio brief                   | A ledger of the answers             |
| [onboarding43](/block/onboarding43) | Workspace setup wizard         | A review of what will be created    |
| [cta92](/block/cta92)               | Tailored subscription          | A confirmation that reads back      |
| [saas108](/block/saas108)           | Branching sales enquiry        | A scoped enquiry, sent              |

### Start with the question you already ask badly

The quickest way in is not to build a new flow. It is to find the place where your product already asks too much at once: the contact form nobody finishes, the filter sidebar nobody touches, the help page that sends everyone to the same inbox. Install the block that fits, replace the demo copy with the questions you already ask on a call, and wire the outcome to whatever handles it today.

The questions are the product decision. The blocks are so that writing them down takes an afternoon rather than a sprint.

## Build it with real blocks

Every section in this post is a component you can copy into your own codebase and own outright.

[Browse blocksBrowse blocks](/blocks)[Read more postsRead more posts](/blog)

## Ship the interface, keep the code.

New blocks, pieces and components every week. Install one with a command and it is yours to edit. No runtime dependency, no upgrade path to fight.

[Browse the libraryBrowse the library](/blocks)[See pricingSee pricing](/pricing)

### Library

* [Blocks](/blocks)
* [Pages](/pages)
* [Pieces](/pieces)
* [Components](/components)
* [Search](/search)

### Learn

* [Docs](/docs)
* [AI & MCP](/docs/mcp)
* [Blog](/blog)
* [Free tools](/tools)
* [What's new?](/changelog)
* [Website Builder](https://beste.co)

### More

* [Pricing](/pricing)
* [Referrals](/referrals)
* [License](/license)
* [GitHub](https://github.com/beste-co/beste-ui)

### Shadcn Blocks

* [Shadcn Hero Blocks](/blocks/hero)
* [Shadcn Feature Blocks](/blocks/feature)
* [Shadcn Pricing Blocks](/blocks/pricing)
* [Shadcn CTA Blocks](/blocks/cta)
* [Shadcn FAQ Blocks](/blocks/faq)
* [Shadcn About Blocks](/blocks/about)
* [Shadcn Stats Blocks](/blocks/stats)
* [Shadcn Footer Blocks](/blocks/footer)
* [Shadcn Navigation Blocks](/blocks/navigation)
* [Shadcn Auth Blocks](/blocks/auth)
* [Shadcn Ecommerce Blocks](/blocks/ecommerce)
* [Shadcn Portfolio Blocks](/blocks/portfolio)
* [Shadcn Showcase Blocks](/blocks/showcase)
* [Shadcn Careers Blocks](/blocks/careers)
* [Shadcn Onboarding Blocks](/blocks/onboarding)
* [Shadcn Coming Soon Blocks](/blocks/coming-soon)
* [Shadcn Post Blocks](/blocks/post)
* [Shadcn Legal Blocks](/blocks/legal)
* [Shadcn Workflow Blocks](/blocks/workflow)
* [Shadcn News Blocks](/blocks/news)

© 2026, [Beste](https://beste.co). All rights reserved.