---
title: "Create a questions-answers widget"
slug: "create-a-questions-answers-widget"
abstract: "How to create a questions-answers widget by requesting content from Sitecore Search."
---
# Create a questions-answers widget

You can use the Cloud SDK [`search`](https://doc.sitecore.com/sdk/en/developers/005/cloud-sdk/cloud-sdk-reference/cloud-sdk-search.html "@sitecore-cloudsdk/search") package to build search experiences[^UUID-101b62f6-b640-ed11-1057-29646ebb9fb7_N67247b544421a], such as a questions-answers (Q&A) widget. This widget lets you show site visitors an AI-generated answer to their question, and optionally, more questions related to their query.

In this walkthrough, you'll create a simple Q&A widget in a Next.js app. Specifically, you'll request Q&A content from Sitecore Search and display it in the user interface.

The walkthrough describes the typical workflow for creating a questions-answers widget. After completing it, you can tailor the requests and the presentation of the content to your own needs for this Q&A widget, or build [other types of search experiences](https://doc.sitecore.com/sdk/en/developers/005/cloud-sdk/cloud-sdk-concepts/concepts-search-experiences/ideas-for-search-experiences.html "Ideas for search experiences").

This walkthrough describes how to:

:::walkthrough
1. [Request and display questions and answers](https://doc.sitecore.com/sdk/en/developers/005/cloud-sdk/developer-guides/search-experiences/create-a-questions-answers-widget.html#request-and-display-questions-and-answers)
2. [Stylize the user interface](https://doc.sitecore.com/sdk/en/developers/005/cloud-sdk/developer-guides/search-experiences/create-a-questions-answers-widget.html#stylize-the-user-interface)
:::

> [!note]
> **Before you begin**
> -   Make sure the [Questions & Answers capability](https://doc.sitecore.com/search/en/users/search-user-guide/en/the-questions-and-answers-capability.html) is configured in Sitecore Search and that batch generation runs at least once. Creating a question and answer group automatically generates the `rfkid`, which is required for requesting questions and answers.
>
> -   In your code editor, open the root folder of your Next.js app. This walkthrough was tested on Next.js version 15, both for the *Pages Router* and the *App Router*.
>
> -   [Install and initialize](https://doc.sitecore.com/sdk/en/developers/005/cloud-sdk/developer-guides/search-experiences/install-and-initialize-the-cloud-sdk.html "Install and initialize the Cloud SDK") the Cloud SDK and the `events` and `search` packages.
>
> -   Optionally, install Tailwind CSS in your app. This walkthrough uses Tailwind CSS to stylize the user interface, but it is not required to complete the walkthrough.

## Request and display questions and answers

The first step to creating a Q&A widget is to request content from Sitecore Search. In Sitecore Search, widgets[^UUID-101b62f6-b640-ed11-1057-29646ebb9fb7_N6724882fb3f4d] hold this content, so you will request data for a specific widget.

1. In your code editor, open the root folder of your Next.js app.
2. Depending on your router type:
    - If using the *Pages Router* - in the `pages` folder, create a file called `questions.tsx`.
    - If using the *App Router* - in the `app` folder, create a subfolder called `questions`. Then, in the `questions` folder, create a file called `page.tsx`.
3. To import everything from the `search` package that's required to request data from Sitecore Search:
    - If using the *Pages Router* - paste the following code into `questions.tsx`:

        ```typescript
        import { useState, useEffect } from "react";
        import {
          Context,
          getWidgetData,
          QuestionsAnswersWidgetItem,
          WidgetRequestData
        } from "@sitecore-cloudsdk/search/browser";

        export default function QuestionsPage(){
          return (<></>)
        }
        ```

    - If using the *App Router* - paste the following code into `page.tsx`:

        ```typescript
        "use client";
        import { useState, useEffect } from "react";
        import {
          Context,
          getWidgetData,
          QuestionsAnswersWidgetItem,
          WidgetRequestData,
        } from "@sitecore-cloudsdk/search/browser";

        export default function QuestionsPage(){
          return (<></>)
        }
        ```

4. Inside the default function, directly before the `return` statement, paste the following code then save your changes:

    ```typescript
    // Create state variables to store the received data and loading state:
    const [isLoading, setIsLoading] = useState(true);
    const [mainQuestion, setMainQuestion] = useState<any>({}); // In production, replace `any` with a specific interface
    const [relatedQuestions, setRelatedQuestions] = useState<any[]>([]); // In production, replace `any[]` with a specific interface

    // Perform the initial data request:
    useEffect(() => {
      async function fetchData() {
        const widgetRequest = new QuestionsAnswersWidgetItem(
          "content",
          "rfkid_mygroup",
          {
            keyphrase: "What is sitecoreai",
            exactAnswer: {},
            relatedQuestions: {
              limit: 5,
            },
          }
        );

        // widgetRequest.sources = ["12345"]; // Optionally, return results only from specific sources

        // Create a new context with the locale set to "EN" and "us".
        // Depending on your Sitecore Search configuration, using `Context` might be optional:
        const context = new Context({
          locale: { language: "EN", country: "us" },
        });

        // Call the `getWidgetData` function with the widget request and the context to request the data:
        const response = await getWidgetData(
          new WidgetRequestData([widgetRequest]),
          context
        );

        if (!response) return console.warn("No search results found.");

        // Set the received data to the state variables:
        setMainQuestion(response.widgets?.[0].answer || {});
        setRelatedQuestions(response.widgets?.[0].related_questions || []);
        setIsLoading(false);
      }
      fetchData();
    }, []);
    ```

    This code requests Q&A data from Sitecore Search. Specifically, the code:

    - Creates an object called `widgetRequest` using the `QuestionsAnswersWidgetItem` class. This object describes a questions-answers widget[^UUID-101b62f6-b640-ed11-1057-29646ebb9fb7_N67247c59c68b122qa] with the widget ID[^UUID-101b62f6-b640-ed11-1057-29646ebb9fb7_N67248fcfc8b58] of `"rfkid_mygroup."` It is configured to find an answer for the keyphrase *"What is sitecoreai"*. It also retrieves maximum five other questions related to this query.

        > [!note]
        > **XM Cloud is now SitecoreAI**
        > Some code examples, images, and UI labels may still use XM Cloud while engineering assets are being updated.

        > [!note]
        > The `rfkid` value for a questions and answers widget is generated automatically when a group is created, and can be found in the **Questions and Answers Groups** [menu page in Search](https://doc.sitecore.com/search/en/users/search-user-guide/en/the-questions-and-answers-capability.html). For example, `rfkid_mygroup`.

    - Runs the [`getWidgetData`](https://doc.sitecore.com/sdk/en/developers/005/cloud-sdk/cloud-sdk-reference/cloud-sdk-search/cloud-sdk-search-browser/functions/cloud-sdk-search-browser-getwidgetdata.html "getWidgetData") function to request data associated with `widgetRequest`, and saves the data that Sitecore Search returns into the `response` variable.
    - Stores the returned main question and related questions, if any, in the `mainQuestion` and `relatedQuestions` state variables.
5. Update the `return` statement to return the following, then save your changes:

    ```typescript
    if (isLoading) {
      return <div>Loading...</div>;
    }

    return (
      <div>
        <h2>Question</h2>
        <p>{mainQuestion?.question}</p>
        <h2>Answer</h2>
        <p>{mainQuestion?.answer}</p>
        <h3>Related Questions</h3>
        <ul>
          {relatedQuestions.map((question: any) => (
            <li key={question.id}>
              <h4>{question.question}</h4>
              <p>{question.answer}</p>
            </li>
          ))}
        </ul>
      </div>
    );
    ```

    This code displays a loading message while retrieving data. After the data is retrieved, it iterates through the contents of the state variables and displays the main question and answer, followed by a list of related questions.

6. In your terminal, enter `npm run dev` to start your Next.js app, then open the page you created in your web browser. A main question and a list of related questions display. This is the content you requested from Sitecore Search.
7. Open your web browser's developer tools and on the **Network** tab, start recording network activity.

    You'll use the **Network** tab while building search experiences to check the network requests you make to Sitecore.

8. In your web browser, reload the page and then find a network request made to `edge-platform.sitecorecloud.io/v1/search`. This is the request you made for the Q&A content.

## Stylize the user interface

After requesting and displaying the content, you continue updating your code to stylize the user interface in your app.

To stylize the user interface:

> [!note]
> **XM Cloud is now SitecoreAI**
> Some code examples, images, and UI labels may still use XM Cloud while engineering assets are being updated.

1. Depending on your router type:
    - If using the *Pages Router* - replace the contents of `questions.tsx` with the following, then save your changes:

        ```typescript
        import { useEffect, useState } from "react";
        import {
          Context,
          getWidgetData,
          QuestionsAnswersWidgetItem,
          WidgetRequestData,
        } from "@sitecore-cloudsdk/search/browser";

        export default function QuestionsPage() {
          const [isLoading, setIsLoading] = useState(true);
          const [mainQuestion, setMainQuestion] = useState<any>({}); // In production, replace `any` with a specific interface
          const [relatedQuestions, setRelatedQuestions] = useState<any[]>([]); // In production, replace `any[]` with a specific interface

          useEffect(() => {
            async function fetchData() {
              const widgetRequest = new QuestionsAnswersWidgetItem(
                "content",
                "rfkid_mygroup",
                {
                  keyphrase: "What is xm cloud",
                  exactAnswer: {},
                  relatedQuestions: {
                    limit: 5,
                  },
                }
              );

              // widgetRequest.sources = ["12345"]; // Optionally, return results only from specific sources

              const context = new Context({
                locale: { language: "EN", country: "us" },
              });

              const response = await getWidgetData(
                new WidgetRequestData([widgetRequest]),
                context
              );

              if (!response) return console.warn("No search results found");
              setMainQuestion(response.widgets?.[0].answer || {});
              setRelatedQuestions(response.widgets?.[0].related_questions || []);
              setIsLoading(false);
            }
            fetchData();
          }, []);

          if (isLoading) {
            return <div>Loading ...</div>;
          }

          return (
            <div className="bg-[image:var(--sc-bg)] p-4 flex items-center justify-center flex-col">
              <div className="container mx-auto">
                <div className="rounded-md border border-gray-200 dark:border-gray-600 p-3 my-5 shadow-md shadow-slate-300 dark:shadow-slate-500">
                  <div className="p-3 dark:text-gray-100">
                    <h2 className="text-lg font-bold ">{mainQuestion?.question}</h2>
                    <p className="mt-2 text-sm">{mainQuestion?.answer}</p>
                  </div>
                  <div className="w-full mt-4 px-2">
                    <h3 className="text-md font-bold mb-4">People also ask ...</h3>
                    {relatedQuestions.map((question: any) => (
                      <div key={question.id} className="w-full cursor-pointer border-b dark:border-b-gray-600 py-4">
                        <div className="w-full flex justify-between gap-x-2 text-left text-sm">
                          <span>{question.question}</span>
                        </div>
                        <div className="pt-5 font-light text-sm">{question.answer}</div>
                      </div>
                    ))}
                  </div>
                </div>
              </div>
            </div>
          );
        };
        ```

    - If using the *App Router* - replace the contents of `page.tsx` with the following, then save your changes:

        ```typescript
        "use client";
        import { useEffect, useState } from "react";
        import {
          Context,
          getWidgetData,
          QuestionsAnswersWidgetItem,
          WidgetRequestData,
        } from "@sitecore-cloudsdk/search/browser";

        export default function QuestionsPage() {
          const [isLoading, setIsLoading] = useState(true);
          const [mainQuestion, setMainQuestion] = useState<any>({}); // In production, replace `any` with a specific interface
          const [relatedQuestions, setRelatedQuestions] = useState<any[]>([]); // In production, replace `any[]` with a specific interface

          useEffect(() => {
            async function fetchData() {
              const widgetRequest = new QuestionsAnswersWidgetItem(
                "content",
                "rfkid_mygroup",
                {
                  keyphrase: "What is xm cloud",
                  exactAnswer: {},
                  relatedQuestions: {
                    limit: 5,
                  },
                }
              );

              // widgetRequest.sources = ["12345"]; // Optionally, return results only from specific sources

              const context = new Context({
                locale: { language: "EN", country: "us" },
              });

              const response = await getWidgetData(
                new WidgetRequestData([widgetRequest]),
                context
              );

              if (!response) return console.warn("No search results found");
              setMainQuestion(response.widgets?.[0].answer || {});
              setRelatedQuestions(response.widgets?.[0].related_questions || []);
              setIsLoading(false);
            }
            fetchData();
          }, []);

          if (isLoading) {
            return <div>Loading ...</div>;
          }

          return (
            <div className="bg-[image:var(--sc-bg)] p-4 flex items-center justify-center flex-col">
              <div className="container mx-auto">
                <div className="rounded-md border border-gray-200 dark:border-gray-600 p-3 my-5 shadow-md shadow-slate-300 dark:shadow-slate-500">
                  <div className="p-3 dark:text-gray-100">
                    <h2 className="text-lg font-bold ">{mainQuestion?.question}</h2>
                    <p className="mt-2 text-sm">{mainQuestion?.answer}</p>
                  </div>
                  <div className="w-full mt-4 px-2">
                    <h3 className="text-md font-bold mb-4">People also ask ...</h3>
                    {relatedQuestions.map((question: any) => (
                      <div key={question.id} className="w-full cursor-pointer border-b dark:border-b-gray-600 py-4">
                        <div className="w-full flex justify-between gap-x-2 text-left text-sm">
                          <span>{question.question}</span>
                        </div>
                        <div className="pt-5 font-light text-sm">{question.answer}</div>
                      </div>
                    ))}
                  </div>
                </div>
              </div>
            </div>
          );
        };
        ```

    This updated code replaces the basic HTML structure in the `return` statement with styled components using Tailwind CSS classes.

2. In your web browser, reload the Q&A page. The webpage now loads with Tailwind CSS styles applied.

## Next steps

You've now successfully created a questions-answers widget.

Next, you can:

- [Learn about the concepts](https://doc.sitecore.com/sdk/en/developers/005/cloud-sdk/cloud-sdk-concepts/concepts-search-experiences.html "Search experiences") that will help you successfully build search experiences. For example, [index documents and attributes](https://doc.sitecore.com/sdk/en/developers/005/cloud-sdk/cloud-sdk-concepts/concepts-search-experiences/search-concepts-for-cloud-sdk-developers/your-indexed-website-content-in-sitecore-search.html "Your indexed website content in Sitecore Search"), [widgets](https://doc.sitecore.com/sdk/en/developers/005/cloud-sdk/cloud-sdk-concepts/concepts-search-experiences/search-concepts-for-cloud-sdk-developers/widgets.html "Widgets"), [context](https://doc.sitecore.com/sdk/en/developers/005/cloud-sdk/cloud-sdk-concepts/concepts-search-experiences/search-concepts-for-cloud-sdk-developers/context.html "Context"), [filters](https://doc.sitecore.com/sdk/en/developers/005/cloud-sdk/cloud-sdk-concepts/concepts-search-experiences/search-concepts-for-cloud-sdk-developers/filters.html "Filters"), and [facets](https://doc.sitecore.com/sdk/en/developers/005/cloud-sdk/cloud-sdk-concepts/concepts-search-experiences/search-concepts-for-cloud-sdk-developers/facets.html "Facets").
- [See examples of other types of search experiences to build](https://doc.sitecore.com/sdk/en/developers/005/cloud-sdk/cloud-sdk-concepts/concepts-search-experiences/ideas-for-search-experiences.html "Ideas for search experiences"), such as recommendations and search suggestions.
- Learn more about [tracking](https://doc.sitecore.com/sdk/en/developers/005/cloud-sdk/cloud-sdk-concepts/concepts-tracking.html "Tracking"), what other [events](https://doc.sitecore.com/sdk/en/developers/005/cloud-sdk/cloud-sdk-concepts/concepts-tracking/events.html "Events") you can capture, and [what the collected data can be used for](https://doc.sitecore.com/sdk/en/developers/005/cloud-sdk/cloud-sdk-concepts/concepts-tracking/working-with-the-collected-data.html "Working with the collected data").
- Explore the reference documentation for the [`search`](https://doc.sitecore.com/sdk/en/developers/005/cloud-sdk/cloud-sdk-reference/cloud-sdk-search.html "@sitecore-cloudsdk/search") package.

[^UUID-101b62f6-b640-ed11-1057-29646ebb9fb7_N67247b544421a]: An interaction that a site visitor has with a Sitecore Search implementation that influences their satisfaction and future behavior.

[^UUID-101b62f6-b640-ed11-1057-29646ebb9fb7_N6724882fb3f4d]: A configurable page component that collects and transmits search-related content to your app. Widgets are set up and published in Sitecore Search, and Cloud SDK developers request widget data in their app. After Sitecore Search responds with the widget data, developers can use the data to display search experiences to site visitors.

[^UUID-101b62f6-b640-ed11-1057-29646ebb9fb7_N67247c59c68b122qa]: A widget that provides an AI-generated answer to the site visitor's question, and, optionally, more related questions.

[^UUID-101b62f6-b640-ed11-1057-29646ebb9fb7_N67248fcfc8b58]: A *widget ID*, also known as the RFK ID, is a unique identifier assigned to every widget in Sitecore Search. Cloud SDK developers use the widget ID when requesting widget data and when capturing *events* that occur in a specific widget.
