> Documentation index: https://www.gooddata.ai/docs/gooddata-ui/llms.txt lists every page of these docs as Markdown.

# Combine the AI Assistant with your app

> Switch the AI Assistant between docked and fullscreen layouts, show the conversations list next to the chat, connect the assistant to a dashboard, and hand it a Semantic Search query.

API reference: [GenAIAssistant()](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-gen-ai/sdk-ui-gen-ai.genaiassistant/index.md) (function), [GenAIConversations()](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-gen-ai/sdk-ui-gen-ai.genaiconversations/index.md) (function), [GenAiStore()](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-gen-ai/sdk-ui-gen-ai.genaistore/index.md) (function)

Place the AI Assistant in your layout and connect it to the components around it. Every example needs the AI Assistant set up in your organization, see [Prerequisites](https://www.gooddata.ai/docs/gooddata-ui/latest/ai-modules/ai-assistant/index.md#prerequisites).

## [Switch between the docked and fullscreen layouts](https://www.gooddata.ai/docs/gooddata-ui/latest/ai-modules/ai-assistant-layout/#switch-between-the-docked-and-fullscreen-layouts)

Use `mode` to switch the assistant between the compact `docked` layout, intended for a side panel, and the wide `fullscreen` layout, intended for a full-page section. `mode` changes only the internal layout; size the container yourself.

Keep the mode in state, and update it from [`ChatModeChangeEvent`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-gen-ai/sdk-ui-gen-ai.chatmodechangeevent/index.md) so that the header's expand and minimize control stays in sync with your chrome:

```tsx
import { useState } from "react";

import {
    GenAIAssistant,
    type GenAIAssistantMode,
    isChatModeChangeEvent,
    type ChatModeChangeEvent,
} from "@gooddata/sdk-ui-gen-ai";

const App = () => {
    const [mode, setMode] = useState<GenAIAssistantMode>("docked");

    return (
        <div className={mode === "fullscreen" ? "my-app-ai-page" : "my-app-ai-panel"}>
            <button onClick={() => setMode(mode === "docked" ? "fullscreen" : "docked")}>
                Toggle fullscreen
            </button>
            <GenAIAssistant
                mode={mode}
                eventHandlers={[
                    {
                        eval: isChatModeChangeEvent,
                        handler: (event: ChatModeChangeEvent) => {
                            setMode(event.mode);
                        },
                    },
                ]}
            />
        </div>
    );
};
```

When the control that toggles the mode lives outside the component tree that renders the assistant, dispatch `setFullscreenAction` through the store dispatcher instead:

```tsx
import { GenAIAssistant, setFullscreenAction } from "@gooddata/sdk-ui-gen-ai";

<GenAIAssistant
    onDispatcher={(dispatch) => {
        dispatch(setFullscreenAction({ isFullscreen: true }));
    }}
/>;
```

On small screens, the assistant always uses the fullscreen layout. [Display mode](https://www.gooddata.ai/docs/gooddata-ui/latest/ai-modules/ai-assistant/index.md#display-mode) describes the events in detail.

## [Show the conversations list next to the chat](https://www.gooddata.ai/docs/gooddata-ui/latest/ai-modules/ai-assistant-layout/#show-the-conversations-list-next-to-the-chat)

Render [`GenAIConversations`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-gen-ai/sdk-ui-gen-ai.genaiconversations/index.md) and [`GenAIAssistant`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-gen-ai/sdk-ui-gen-ai.genaiassistant/index.md) inside one [`GenAiStore`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-gen-ai/sdk-ui-gen-ai.genaistore/index.md). Both read the active conversation from the store, so selecting a conversation in the list switches the chat without extra wiring.

```tsx
import { GenAiStore, GenAIConversations, GenAIAssistant, startNewConversationAction } from "@gooddata/sdk-ui-gen-ai";

import "@gooddata/sdk-ui-gen-ai/styles/css/main.css";

const App = () => {
    let dispatcher: (action: unknown) => void = () => undefined;

    return (
        <GenAiStore onDispatcher={(storeDispatch) => (dispatcher = storeDispatch)}>
            <GenAIConversations
                onConversationSelect={(conversation) => {
                    // The selection is already applied to the shared store;
                    // use the callback for your own side effects, for example routing
                    console.log(`Selected conversation: ${conversation.title}`);
                }}
            />
            <GenAIAssistant />
            <button onClick={() => dispatcher(startNewConversationAction())}>New conversation</button>
        </GenAiStore>
    );
};
```

### [Split layout with the conversations list](https://www.gooddata.ai/docs/gooddata-ui/latest/ai-modules/ai-assistant-layout/#split-layout-with-the-conversations-list)

This layout puts the list and the chat in separate panels, with a shared "new conversation" button between them:

```tsx
import { type CSSProperties, useState } from "react";

import {
    GenAiStore,
    GenAIConversations,
    GenAIAssistant,
    startNewConversationAction,
} from "@gooddata/sdk-ui-gen-ai";

import "@gooddata/sdk-ui-gen-ai/styles/css/main.css";

export function App() {
    const [dispatcher, setDispatcher] = useState<(action: unknown) => void>(() => {
        return (() => undefined) as (action: unknown) => void;
    });

    return (
        <GenAiStore
            onDispatcher={(storeDispatch) => {
                setDispatcher(() => storeDispatch);
            }}
        >
            <div style={getConversationsStyle()}>
                <GenAIConversations />
            </div>
            <button
                style={getButtonStyle()}
                onClick={() => {
                    dispatcher(startNewConversationAction());
                }}
            >
                NEW CONVERSATION
            </button>
            <div style={getAssistantStyle()}>
                <GenAIAssistant />
            </div>
        </GenAiStore>
    );
}

function getConversationsStyle(): CSSProperties {
    return {
        position: "absolute",
        width: "350px",
        left: "30px",
        top: "30px",
        bottom: "30px",
        border: "3px solid #ccc",
        borderRadius: "10px",
        overflow: "auto",
    };
}

function getAssistantStyle(): CSSProperties {
    return {
        position: "absolute",
        width: "500px",
        right: "30px",
        top: "30px",
        bottom: "30px",
        border: "3px solid #ccc",
        borderRadius: "10px",
        overflow: "hidden",
    };
}

function getButtonStyle(): CSSProperties {
    return {
        position: "absolute",
        right: "50%",
        top: "50%",
    };
}
```

How the integration works:

- [`GenAiStore`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-gen-ai/sdk-ui-gen-ai.genaistore/index.md) provides a shared dispatcher and synchronized state for both components.
- [`GenAIConversations`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-gen-ai/sdk-ui-gen-ai.genaiconversations/index.md) and [`GenAIAssistant`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-gen-ai/sdk-ui-gen-ai.genaiassistant/index.md) read the active conversation from that store, so selecting a
  conversation in the list switches the assistant pane without extra wiring.
- The shared "new conversation" button dispatches `startNewConversationAction` once through the store dispatcher,
  keeping both panels in sync.

## [Build your own conversation list](https://www.gooddata.ai/docs/gooddata-ui/latest/ai-modules/ai-assistant-layout/#build-your-own-conversation-list)

To render the list yourself, read the conversations from the store with the `@alpha` hooks `useGenAiConversations`, `useGenAiConversationsLoaded`, and `useGenAiCurrentConversation`, and switch the conversation with `setCurrentConversationAction`.

```tsx
import {
    GenAiStore,
    useGenAiConversations,
    setCurrentConversationAction,
    useGenAiDispatcher,
} from "@gooddata/sdk-ui-gen-ai";

const MyConversations = () => {
    const dispatch = useGenAiDispatcher();
    const conversations = useGenAiConversations();

    return (
        <ul>
            {conversations?.map((conv) => (
                <li
                    key={conv.localId}
                    onClick={() => dispatch(setCurrentConversationAction({ conversation: conv }))}
                >
                    {conv.title}
                </li>
            ))}
        </ul>
    );
};

const App = () => (
    <GenAiStore>
        <MyConversations />
    </GenAiStore>
);
```

## [Connect the assistant to a dashboard](https://www.gooddata.ai/docs/gooddata-ui/latest/ai-modules/ai-assistant-layout/#connect-the-assistant-to-a-dashboard)

[`GenAIAssistant`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-gen-ai/sdk-ui-gen-ai.genaiassistant/index.md) can be integrated with the [`Dashboard`](https://www.gooddata.ai/docs/gooddata-ui/latest/dashboard-plugins/dashboard/index.md) component through the `dashboardSelector` prop. This allows the AI Assistant to react to the dashboard state (filters, widgets, etc.) and build ambient context automatically.

To do this, you need to capture the [`DashboardSelectorEvaluator`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-dashboard/sdk-ui-dashboard.dashboardselectorevaluator/index.md) from the `onStateChange` callback of the [`Dashboard`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-dashboard/sdk-ui-dashboard.dashboard/index.md) component and pass it to the [`GenAIAssistant`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-gen-ai/sdk-ui-gen-ai.genaiassistant/index.md).
If the dashboard is closed or unmounted, pass `null` to clear the previously derived ambient dashboard context.

```tsx
import { useCallback, useState } from "react";
import { Dashboard, DashboardSelectorEvaluator, DashboardState } from "@gooddata/sdk-ui-dashboard";
import { GenAIAssistant, GenAiStore } from "@gooddata/sdk-ui-gen-ai";

export function App() {
    const dashboardId = "<dashboard-id>";
    const [evaluator, setEvaluator] = useState<DashboardSelectorEvaluator | undefined>();

    const onStateChange = useCallback((state: DashboardState) => {
        // Capture the selector evaluator from the state
        const dashboardSelect: DashboardSelectorEvaluator = (select) => select(state);
        setEvaluator(() => dashboardSelect);
    }, []);

    return (
        <>
            <GenAiStore>
                <GenAIAssistant dashboardSelector={evaluator} />
            </GenAiStore>
            <Dashboard dashboard={dashboardId} onStateChange={onStateChange} />
        </>
    );
}
```

## [Run Key Driver Analysis on chat visualizations](https://www.gooddata.ai/docs/gooddata-ui/latest/ai-modules/ai-assistant-layout/#run-key-driver-analysis-on-chat-visualizations)

Mount [`GenAiKeyDriverAnalysis`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-gen-ai/sdk-ui-gen-ai.genaikeydriveranalysis/index.md) next to [`GenAIAssistant`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-gen-ai/sdk-ui-gen-ai.genaiassistant/index.md) inside the same [`GenAiStore`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-gen-ai/sdk-ui-gen-ai.genaistore/index.md). See [Key Driver Analysis component](https://www.gooddata.ai/docs/gooddata-ui/latest/ai-modules/ai-assistant/index.md#key-driver-analysis-component).

## [Hand a Semantic Search query to the assistant](https://www.gooddata.ai/docs/gooddata-ui/latest/ai-modules/ai-assistant-layout/#hand-a-semantic-search-query-to-the-assistant)

Let users move from a search to a conversation about the same query: put [`FooterButtonAiAssistant`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-semantic-search/sdk-ui-semantic-search.footerbuttonaiassistant/index.md) in the footer of [`SemanticSearch`](https://www.gooddata.ai/docs/gooddata-ui/latest/ai-modules/semantic-search/index.md), and when the user clicks it, send the query to the assistant through the store dispatcher.

You need:

- [`SemanticSearch`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-semantic-search/sdk-ui-semantic-search.semanticsearch/index.md) and [`FooterButtonAiAssistant`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-semantic-search/sdk-ui-semantic-search.footerbuttonaiassistant/index.md) from `@gooddata/sdk-ui-semantic-search`
- [`GenAIAssistant`](https://www.gooddata.ai/docs/gooddata-ui/latest/api-reference/sdk-ui-gen-ai/sdk-ui-gen-ai.genaiassistant/index.md), the `clearThreadAction` and `newMessageAction` actions, and the `makeUserItem` helper from `@gooddata/sdk-ui-gen-ai`

```tsx
import { useCallback, useEffect, useState } from "react";
import { SemanticSearch, FooterButtonAiAssistant } from "@gooddata/sdk-ui-semantic-search";
import {
    GenAIAssistant,
    type GenAIAssistantProps,
    clearThreadAction,
    newMessageAction,
    makeUserItem,
} from "@gooddata/sdk-ui-gen-ai";

type ChatDispatcher = Parameters<NonNullable<GenAIAssistantProps["onDispatcher"]>>[0];

const MyCustomSearchComponentWithAiAssistant = () => {
    const [chatDispatcher, setDispatcher] = useState<ChatDispatcher | null>(null);
    const [askedQuestion, setAskedQuestion] = useState("");
    const onDispatcher = useCallback(
        (dispatcher: ChatDispatcher) => {
            setDispatcher(() => dispatcher);
        },
        [setDispatcher],
    );

    useEffect(() => {
        if (!chatDispatcher || !askedQuestion) {
            return;
        }

        chatDispatcher(clearThreadAction());
        chatDispatcher(newMessageAction(makeUserItem({ type: "text", text: askedQuestion })));
    }, [chatDispatcher, askedQuestion]);

    return (
        <>
            <SemanticSearch
                onSelect={(item) => {
                    console.log("Selected item:", item);
                }}
                objectTypes={["dashboard", "visualization", "metric"]}
                renderFooter={({ value }, { closeSearch }) => (
                    <FooterButtonAiAssistant
                        onClick={() => {
                            // You can extend the search query with additional context.
                            setAskedQuestion(`Build new visualization based on: ${value}`);
                            closeSearch();
                        }}
                    />
                )}
            />
            <GenAIAssistant onDispatcher={onDispatcher} />
        </>
    );
};
```

When the user clicks the button, the search dropdown closes, the chat thread is cleared, and the query goes to the assistant as a user message. Users can still pick a search result through `onSelect`.
