shadimage
contentbit
componentastro-faqsource: ok · hit

FAQ Block (Astro)

Accordion of question/answer pairs.

npx shadcn add "https://contentbit.dev/r/astro-faq.json"
free

Where it goes

faq

Interaction

accordion

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
@contentbit/core@contentbit/blocks@contentbit/astro

Files1 · source cached 10/15/2026, 3:42:06 AM

src/astro/faq.astro
---
import type { FaqData, FaqItemData } from '@contentbit/blocks'
import type { ValidatedBlockNode } from '@contentbit/core'
import type { AstroBlockContext } from '@contentbit/astro'

import { isValidatedBlock } from '@contentbit/core'

interface Props {
  node: ValidatedBlockNode<unknown>
  ctx: AstroBlockContext
}

const { node, ctx } = Astro.props
const data = node.data as FaqData
const items = await Promise.all(
  data.blocks.map(async (item) => ({
    question: String(item.props.question),
    html: await ctx.renderMarkdown(isValidatedBlock(item) ? (item.data as FaqItemData).markdown : item.body),
  })),
)

// Inline lucide path: chevron-down.
const CHEVRON = '<path d="m6 9 6 6 6-6"/>'

// Native <details> means items open independently (the React pack's accordion
// is single-open). Add name="faq" to the <details> elements for exclusivity.
---

<div data-cb-styled class="bg-card my-6 rounded-lg border px-4">
  {
    items.map((item, i) => (
      <details class:list={['group', i < items.length - 1 && 'border-b']}>
        <summary class="flex cursor-pointer list-none items-center justify-between gap-4 py-4 text-left text-sm font-medium [&::-webkit-details-marker]:hidden">
          {item.question}
          <svg
            xmlns="http://www.w3.org/2000/svg"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
            class="text-muted-foreground size-4 shrink-0 transition-transform group-open:rotate-180"
            aria-hidden="true"
            set:html={CHEVRON}
          />
        </summary>
        <div class="text-muted-foreground pb-4 text-sm leading-relaxed" set:html={item.html} />
      </details>
    ))
  }
</div>

More from contentbit

Browse all components in this library →