React Components

Build a help center with the React components

Atualizado em 30 visualizações

A help center has three kinds of page: an index of categories with a search box, a page per category, and the article itself with its breadcrumbs, table of contents and a "Was this article helpful?" vote. @kookee/react has the components for all three. The examples use the Next.js App Router, with the SDK client and its config from lib/kookee.ts; see Using the React components with Next.js, TanStack Start or Vite for that file and for other setups.

The index page

tsx
// app/help/page.tsx
import { HelpCategoryGrid } from '@kookee/react/help';
import { HelpSearchBox } from '@kookee/react/help/client';
import { kookee, kookeeConfig } from '@/lib/kookee';

export default async function HelpPage() {
  const categories = await kookee.help.categories();

  return (
    <>
      <HelpSearchBox config={kookeeConfig} href="/help/{categorySlug}/{slug}" />
      <HelpCategoryGrid categories={categories} href="/help/{categorySlug}" />
    </>
  );
}

HelpCategoryGrid renders a card per category with its icon, name, description and article count. The grid fills the width with cards at least --kookee-card-min wide, 17rem unless you change it.

Search as you type

HelpSearchBox searches your published help articles by meaning, not keywords, and lists the matches under the input, each with the passage that matched.

  • It searches from two characters, 300 ms after typing stops, and aborts a search still in flight when the query changes.

  • It announces loading, the number of results and errors to screen readers.

  • searchLocale is the content locale to search in. locale only formats the result count.

  • query, defaultQuery and onQueryChange keep the query in sync with the URL.

  • showCategory (from version 1.9.3) prints each result's category name above its title.

  • onResultClick is called with the result a visitor opened, which is useful for analytics. It is a function, so in Next.js render the box from a 'use client' file of your own when you pass it.

For a search UI of your own, useHelpSearchBox({ config }) returns { query, setQuery, results, status, error } with the same debounce and cancellation. It is a different hook from the root's useHelpSearch, which needs a KookeeProvider.

The category page

tsx
// app/help/[category]/page.tsx
import { notFound } from 'next/navigation';
import { HelpArticleList, HelpBreadcrumbs, HelpCategoryHeader, HelpCategoryNav } from '@kookee/react/help';
import { kookee } from '@/lib/kookee';

export default async function CategoryPage({ params }: { params: Promise<{ category: string }> }) {
  const { category: slug } = await params;
  const [categories, articles] = await Promise.all([
    kookee.help.categories(),
    kookee.help.list({ category: slug, limit: 100 }),
  ]);
  const category = categories.find((c) => c.slug === slug);
  if (!category) notFound();

  return (
    <div className="help-layout">
      <HelpCategoryNav categories={categories} activeCategorySlug={slug} href="/help/{categorySlug}" showCounts />
      <main>
        <HelpBreadcrumbs homeHref="/help" category={category} />
        <HelpCategoryHeader category={category} />
        <HelpArticleList articles={articles.data} href="/help/{categorySlug}/{slug}" />
      </main>
    </div>
  );
}
  • HelpCategoryNav lists every category as a link, with its icon and, with showCounts, its article count. The active one gets aria-current and data-active="true".

  • HelpCategoryHeader renders the icon and name as the page heading, the description, and the article count unless you pass showCount={false}.

  • HelpBreadcrumbs renders "Help Center / Category" as an ordered list inside a <nav>. The last crumb is the current page, unlinked, with aria-current="page". before adds crumbs in front, such as your site's home page: before={[{ label: 'Home', href: '/' }]}, and separator replaces the / between them.

  • HelpArticleList renders a card per article with its title and excerpt. Given search results instead, it shows the passage that matched. For a list that mixes categories, such as popular articles on the index page, render each item as <HelpArticleCard article={item} href="/help/{categorySlug}/{slug}" showCategory /> through renderItem: showCategory (from version 1.9.3) prints the category name above the title, as text.

The article page

tsx
// app/help/[category]/[slug]/page.tsx
import { HelpArticle, HelpBreadcrumbs } from '@kookee/react/help';
import { HelpfulVote } from '@kookee/react/help/client';
import { extractHeadings } from '@kookee/react/entry';
import { TrackedTableOfContents } from '@kookee/react/entry/client';
import { kookee, kookeeConfig } from '@/lib/kookee';

export default async function ArticlePage({ params }: { params: Promise<{ slug: string }> }) {
  const article = await kookee.help.getBySlug((await params).slug);

  return (
    <>
      <HelpBreadcrumbs homeHref="/help" category={article.category} categoryHref="/help/{categorySlug}" article={article} />
      <HelpArticle article={article} classNames={{ content: 'prose' }} />
      <TrackedTableOfContents headings={extractHeadings(article.contentHtml)} offset={64} resetKey={article.id} />
      <HelpfulVote
        entryId={article.id}
        reactions={article.reactions}
        config={kookeeConfig}
        storageKey="kookee:helpful-votes"
        showCounts
      />
    </>
  );
}

HelpArticle renders the title, an "Updated" line with the date the article last changed, and the body. From version 1.9.3 it takes headerClassNames for the header's title and updated elements, and HelpArticleViews renders the article's view count, such as "42 views", wherever you place it:

tsx
import { HelpArticle, HelpArticleViews } from '@kookee/react/help';

<HelpArticle article={article} classNames={{ content: 'prose' }} headerClassNames={{ title: 'text-3xl' }} />
<HelpArticleViews article={article} />

The views label translates the count, with one form per plural category: labels={{ views: { one: '{count} vue', other: '{count} vues' } }}.

Handle a missing slug with notFound(), as in Build a blog with the React components, and see there for the table of contents' offset and resetKey.

"Was this article helpful?"

HelpfulVote asks the question with a yes and a no button. Votes are stored as the article's yes and no reactions, so you can read them back from article.reactions through the API.

  • A visitor has one vote. Choosing the other answer removes the first.

  • The visitor's vote is remembered in localStorage. Give it its own storageKey, as above, when the site also shows Reactions, so the two widgets do not share their memory.

  • showCounts shows how many people chose each answer. Leave it out to hide the numbers.

  • The root carries data-vote, which is yes, no or none, so you can show a thank-you message with CSS alone.

  • icons adds an icon before each answer, and the helpfulQuestion, helpfulYes and helpfulNo labels replace the text.

Like reactions, votes are anonymous. They tell you which articles are working, not exactly how many people agree.

A sidebar of the category's other articles is the same list with a smaller item:

tsx
import { HelpArticleList, HelpArticleTitle } from '@kookee/react/help';

const siblings = await kookee.help.list({ category: article.category?.slug, limit: 20 });

<HelpArticleList
  articles={siblings.data.filter((a) => a.id !== article.id)}
  renderItem={(item) => <HelpArticleTitle article={item} href="/help/{categorySlug}/{slug}" headingLevel={3} />}
/>;

Loading states

HelpCategoryGridSkeleton and HelpArticleListSkeleton render the grid's and the list's own layout with placeholders, so the page keeps its shape while content loads.

Pair it with the AI chat

The components cover reading and searching. For questions your articles answer only in part, add the AI chat widget, which answers from the same published articles and links to them: see Building a help center with search and AI chat.

Este artigo foi útil?