Skip to content

Full-page assistant

A2AChat is the same chat UI as the floating widget, but it sits in your page layout. Use it for a /chat or /assistant page, or inside a dashboard.

It uses the same two server routes as the floating widget (/api/a2a and /api/a2a/config). If you’ve already followed the chatbot guide, the server side is done.

src/app/chat/page.tsx
"use client";
import { A2AChat } from "@gnomondigital/nebulas-kit-react";
export default function ChatPage() {
return (
<div className="container mx-auto max-w-4xl py-8 px-4">
<A2AChat
a2aEndpoint="/api/a2a"
configEndpoint="/api/a2a/config"
labels={{
title: "Assistant",
description: "Ask anything about our products",
newChat: "New chat",
placeholder: "Ask anything…",
}}
suggestedPrompts={[
"Summarize our return policy",
"Which plan fits a team of 10?",
]}
showAttachFileButton
/>
</div>
);
}
  • layout="page" (default): the chat takes the height of the viewport.
  • layout="panel": the chat fills its parent. Use it in a sidebar, tab or card that has a fixed height:
<div className="h-[600px] rounded-lg border">
<A2AChat layout="panel" a2aEndpoint="/api/a2a" configEndpoint="/api/a2a/config" />
</div>

By default the chat only talks to the agent. To also save each conversation and its messages in your Nebulas project (so you can review them in Nebulas), pass nebulasBaseUrl. It points at a proxy route for the Nebulas conversation API:

  1. Add the catch-all Nebulas proxy. It needs a signed-in session because it writes records on behalf of a user:

    src/app/api/nebulas/[...path]/route.ts
    import "@/lib/nebulas";
    import { createNebulasProxyHandler } from "@gnomondigital/nebulas-kit-core/nextjs";
    import { auth0 } from "@/lib/auth";
    const handler = createNebulasProxyHandler({
    auth: auth0,
    audience: process.env.AUTH0_A2A_AUDIENCE,
    apiKey: process.env.NEBULAS_API_KEY,
    });
    export const GET = handler;
    export const POST = handler;
    export const PUT = handler;
    export const PATCH = handler;
    export const DELETE = handler;
  2. Pass the route to the component:

    <A2AChat
    a2aEndpoint="/api/a2a"
    configEndpoint="/api/a2a/config"
    nebulasBaseUrl="/api/nebulas"
    user={{ name: user.name, picture: user.picture }}
    />

The first message creates a conversation in the project that /api/a2a/config returns (nebulasProjectId). The following messages are added to it. New chat starts a new conversation.

For quick internal tools, you can let people sign in from the page and send their token with each chat request. useA2AAuth keeps the token in sessionStorage:

"use client";
import { A2AChat, useA2AAuth } from "@gnomondigital/nebulas-kit-react";
export default function InternalChat() {
const { token, login, error, isLoading } = useA2AAuth({
authEndpoint: "/api/a2a/auth",
});
if (!token) {
return (
<form
onSubmit={(e) => {
e.preventDefault();
const f = new FormData(e.currentTarget);
login(String(f.get("username")), String(f.get("password"))).catch(() => {}); // shown via `error`
}}
>
<input name="username" />
<input name="password" type="password" />
<button disabled={isLoading}>Sign in</button>
{error && <p>{error}</p>}
</form>
);
}
return (
<A2AChat
a2aEndpoint="/api/a2a"
configEndpoint="/api/a2a/config"
getAuthHeaders={() => ({ Authorization: `Bearer ${token}` })}
authDependency={token} // reloads the config after login
/>
);
}

authEndpoint is a route you write. It receives POST { username, password }, exchanges them with your identity provider (for example the Auth0 password grant), and returns { "access_token": "…" }. On failure, return a non-2xx status with { "error": "…" }.