---
title: Questions
description: The questions a page expects to be asked, written beside the prose that provokes them and answerable in place.
source: https://adamjurek.com/components/questions
---

## Home


A page explains something and a reader arrives with a question about it. This
is the list of the three or four that actually get asked, written by whoever
wrote the page.

Given an `onAsk`, each one is a button that puts itself to an assistant;
without one it is a list of questions, which is a legitimate document and
exactly what renders on the server.

<!-- ::start:showcase demo="questions" height="320" -->
<!-- ::end:showcase -->

## What it does not do

It does not answer anything. It has no opinion about what is on the other end
of `onAsk`, which is why it is in the library at all: a component that knew
about this site's assistant would be a page, not a component.

It does not store, rank or count. "Popular" here means "the author knows these
get asked", not a measurement - there is no analytics behind it, and a list
that claimed to be ranked by real traffic while being hand-written would be
worse than an honest hand-written list.

It does not deduplicate. Two identical questions on one page are a mistake in
the content, and hiding it would only make it harder to find.


## Install

<!-- ::start:tabs -->

### TanStack

```shell
tanstack add https://adamjurek.com/r/tanstack/questions.json
```

### shadcn

```shell
pnpm dlx shadcn@latest add https://adamjurek.com/r/shadcn/questions.json
```

### pnpm

```shell
pnpm add @sushindustries/ui @sushindustries/atoms
```

<!-- ::end:tabs -->

## What you get

| | |
| --- | --- |
| Version | 0.1.0 |
| Category | content · Documents |
| Files | `questions.tsx` |
| Dependencies | None |
| Tags | assistant, no-deps |

> [!NOTE] No runtime dependencies
> It brings nothing with it beyond the stylesheet.

## Get Started


Install commands are on Home, attached from the registry - they are not written
here, because a second copy is a copy that goes stale. This tab starts after the
install worked.

## Use it

```tsx
import { Questions } from "@sushindustries/ui";

export function Help() {
	return (
		<Questions
			heading="Common questions"
			questions={[
				"How do I install a component?",
				"Do I need the whole library?",
			]}
		/>
	);
}
```

## What you should see

A heading followed by a list of the questions, each rendered as plain text -
this example has no `onAsk`, so nothing is clickable. Pass `onAsk` and the
same questions become buttons.

## If nothing happens

An empty `questions` array renders nothing at all, heading included - that's
deliberate, not a bug. If the heading shows but the list doesn't look like
buttons, check whether `onAsk` is actually being passed; without it the items
are `<span>`s by design.


## Guides


## Why it is built this way

The questions are content, not configuration. They live in the Markdown of the
page they belong to, in a `questions` block:

```markdown
<!-- ::start:questions heading="Common questions" -->

- How do I install a component?
- Do I need the whole library?
- What happens when a component updates?

<!-- ::end:questions -->
```

Written as a plain Markdown list, so a page carrying this block still reads
correctly as a document, and the questions turn up in the diff of the page they
describe rather than in a table somewhere else. A question that no longer
matches the page is visible to whoever is editing the page.

The block reads the questions out of the rendered list rather than re-parsing
the source, and ignores anything that is not a list item. A stray paragraph
inside the block is a typo, and rendering it as a question would put words in
the author's mouth.

Pressing one writes to a store that the assistant reads. The two are far apart
in the tree - a block is somewhere inside a rendered document, the assistant is
mounted once in the site chrome - and threading a callback between them would
mean every route that renders Markdown knowing the assistant exists.

## Props

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `questions` | `readonly string[]` | required | The questions. An empty list renders nothing at all. |
| `heading` | `string` | none | What to call the list. "Common questions" and "Try asking" are different promises. |
| `onAsk` | `(question: string) => void` | none | Put the question to something that can answer it. Without it, each entry is a list item rather than a button. |
| `level` | `2 \| 3 \| 4` | `2` | Heading level, so the page outline stays correct. |


## API


<!-- generated:api -->

## Props

| Prop | Type | Default | Does |
| --- | --- | --- | --- |
| `heading?` | `string` | - | What to call the list. A heading rather than a hard-coded string because "Common questions" and "Try asking" are different promises. |
| `questions` | `readonly string[]` | - | Empty renders nothing, heading included. Each question is its own key, so duplicates collide. |
| `onAsk?` | `(question: string) => void` | - | Put the question to something that can answer it. Given this, each entry is a button; without it, a list item. |
| `level?` | `2 \| 3 \| 4` | `2` | Heading level, so the page outline stays correct. Defaults to `h2`. |

<!-- /generated:api -->

## Notes

`onAsk` is the only thing that changes what renders - given it, each question
is a `<button>`; without it, a `<span data-static="true">`. There's no prop
to force one or the other independently of whether a handler exists.

`questions` is keyed by its own string, so two identical questions in one
list collide and React only renders one. That's a content mistake to fix in
the list, not something to route around with an index key.


## Examples


Examples are the tab where the component is shown doing a job, not
demonstrating a prop. The API tab already lists the props.

<!-- ::start:showcase demo="questions" height="420" -->
<!-- ::end:showcase -->

Press Compare. The frames are real viewports, so a layout that breaks at 320
breaks here too rather than in somebody's hands.

## In a page

```tsx
import { Questions } from "@sushindustries/ui";
import { useAssistant } from "../hooks/use-assistant";

export function DocsFooter() {
	const { ask } = useAssistant();

	return (
		<footer className="container mt-12">
			<Questions
				heading="Try asking"
				questions={[
					"What does renderLink do?",
					"How do I add a package to the registry?",
				]}
				onAsk={ask}
			/>
		</footer>
	);
}
```

## What this example is not

Wired to a real assistant. `useAssistant` here stands in for whatever puts a
question in front of one - this component only calls `onAsk`, it never
answers anything itself.
