Dosu LogoDosu Logo
Ask
Join our Discord
assistant-ui's SpacePublic
assistant-ui
Documentsassistant-ui's Space
part-2
part-2
Type
External
Status
Published
Created
Mar 17, 2026
Updated
Sep 29, 2026
Updated by
Dosu Bot
Source
apps/docs/content/docs/runtimes/langgraph/tutorial/part-2.mdx

In the previous step, we set up the frontend to connect to a LangGraph Cloud endpoint.

In this step, we will set up a component to display stock ticker information.

import Image from "next/image";
import price from "./images/acme-price.png";

Price snapshot

For reference, this is the corresponding code in the backend:

https://github.com/assistant-ui/assistant-ui-stockbroker/blob/main/backend/src/tools.ts#L193C1-L216C3

export const priceSnapshotTool = tool(
  async (input) => {
    const data = await callFinancialDatasetAPI<SnapshotResponse>({
      endpoint: "/prices/snapshot",
      params: {
        ticker: input.ticker,
      },
    });
    return JSON.stringify(data, null);
  },
  {
    name: "price_snapshot",
    description:
      "Retrieves the current stock price and related market data for a given company.",
    schema: z.object({
      ticker: z.string().describe("The ticker of the company. Example: 'AAPL'"),
    }),
  },
);

PriceSnapshotTool#

We create a new file under /components/tools/price-snapshot/PriceSnapshotTool.tsx to define the tool.

First, we define the tool arguments and result types:

type PriceSnapshotToolArgs = {
  ticker: string;
};

type PriceSnapshotToolResult = {
  snapshot: {
    price: number;
    day_change: number;
    day_change_percent: number;
    time: string;
  };
};

Then, we define a tool UI renderer:

"use client";

import type { ToolCallMessagePartComponent } from "@assistant-ui/react";

export const PriceSnapshotToolUI: ToolCallMessagePartComponent<
  PriceSnapshotToolArgs,
  string
> = function PriceSnapshotUI({ args, result }) {
  return (
    <div className="mb-4 flex flex-col items-center">
      <pre className="whitespace-pre-wrap break-all text-center">
        price_snapshot({JSON.stringify(args)})
      </pre>
    </div>
  );
};

This displays the tool name and arguments passed to it, but not the result.

Bind tool UI#

Register the tool UI in a toolkit and pass it to the AssistantRuntimeProvider that the template sets up in app/assistant.tsx:

"use client";

import { AssistantRuntimeProvider, AuiConfig, defineToolkit, Tools } from "@assistant-ui/react";
import { useStreamRuntime } from "@assistant-ui/react-langchain";

import { Thread } from "@/components/assistant-ui/elements/thread.aui";
import { PriceSnapshotToolUI } from "@/components/tools/price-snapshot/PriceSnapshotTool";

const toolkit = defineToolkit({
  price_snapshot: {
    type: "backend",
    render: PriceSnapshotToolUI,
  },
});

export function Assistant() {
  const apiUrl =
    process.env.NEXT_PUBLIC_LANGGRAPH_API_URL ||
    (typeof window !== "undefined"
      ? new URL("/api", window.location.href).href
      : undefined);

  const runtime = useStreamRuntime({
    assistantId: process.env.NEXT_PUBLIC_LANGGRAPH_ASSISTANT_ID!,
    apiUrl,
  });
  const config = AuiConfig({ tools: Tools({ toolkit }) });

  return (
    <AssistantRuntimeProvider runtime={runtime} config={config}>
      <Thread />
    </AssistantRuntimeProvider>
  );
}

Try it out!#

Ask the assistant for the current stock price of Tesla. You should see the following text appear:

price_snapshot({ticker: "TSLA"})

Next, we will visualize the function's result.

Visualizing tool results#

Install dependencies#

The tool result component relies on shadcn/ui's Card component. We will install it as a dependency.

npx shadcn@latest add card

This installs a card UI component in your project.

Add PriceSnapshot#

We create a new file under /components/tools/price-snapshot/price-snapshot.tsx to define the new tool result UI.

"use client";

import { ArrowDownIcon, ArrowUpIcon } from "lucide-react";

import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";

type PriceSnapshotToolArgs = {
  ticker: string;
};

type PriceSnapshotToolResult = {
  price: number;
  day_change: number;
  day_change_percent: number;
  time: string;
};

export function PriceSnapshot({
  ticker,
  price,
  day_change,
  day_change_percent,
  time,
}: PriceSnapshotToolArgs & PriceSnapshotToolResult) {
  const isPositiveChange = day_change >= 0;
  const changeColor = isPositiveChange ? "text-green-600" : "text-red-600";
  const ArrowIcon = isPositiveChange ? ArrowUpIcon : ArrowDownIcon;

  return (
    <Card className="mx-auto w-full max-w-md">
      <CardHeader>
        <CardTitle className="text-2xl font-bold">{ticker}</CardTitle>
      </CardHeader>
      <CardContent>
        <div className="grid grid-cols-2 gap-4">
          <div className="col-span-2">
            <p className="text-3xl font-semibold">${price?.toFixed(2)}</p>
          </div>
          <div>
            <p className="text-muted-foreground text-sm">Day Change</p>
            <p
              className={`flex items-center text-lg font-medium ${changeColor}`}
              <ArrowIcon className="mr-1 h-4 w-4" />$
              {Math.abs(day_change)?.toFixed(2)} (
              {Math.abs(day_change_percent)?.toFixed(2)}%)
            </p>
          </div>
          <div>
            <p className="text-muted-foreground text-sm">Last Updated</p>
            <p className="text-lg font-medium">
              {new Date(time).toLocaleTimeString()}
            </p>
          </div>
        </div>
      </CardContent>
    </Card>
  );
}

Update PriceSnapshotTool#

We will import the new <PriceSnapshot /> component and use it in the render function whenever a tool result is available.

"use client";

import { PriceSnapshot } from "./price-snapshot";
import type { ToolCallMessagePartComponent } from "@assistant-ui/react";

type PriceSnapshotToolArgs = {
  ticker: string;
};

type PriceSnapshotToolResult = {
  snapshot: {
    price: number;
    day_change: number;
    day_change_percent: number;
    time: string;
  };
};

export const PriceSnapshotToolUI: ToolCallMessagePartComponent<
  PriceSnapshotToolArgs,
  string
> = function PriceSnapshotUI({ args, result }) {
  let resultObj: PriceSnapshotToolResult | { error: string };
  try {
    resultObj = result ? JSON.parse(result) : {};
  } catch (e) {
    resultObj = { error: result! };
  }

  return (
    <div className="mb-4 flex flex-col items-center gap-2">
      <pre className="whitespace-pre-wrap break-all text-center">
        price_snapshot({JSON.stringify(args)})
      </pre>
      {"snapshot" in resultObj && (
        <PriceSnapshot ticker={args.ticker} {...resultObj.snapshot} />
      )}
      {"error" in resultObj && (
        <p className="text-red-500">{resultObj.error}</p>
      )}
    </div>
  );
};

Try it out!#

Ask the assistant for the current stock price of Tesla. You should see the tool result appear:

import price2 from "./images/tsla-price.png";

Price snapshot result

Fallback tool UI#

Tools without a custom tool UI render with the ToolFallback built into the thread element, so no additional configuration is needed.

Documents
AG-UI Message History Management
CHANGELOG
CHANGELOG
CHANGELOG
external-store
overview
README
AG-UI Streaming State Management
hooks
index
introduction
overview
part-1
README
README
stockbroker
AI SDK Integration
ai-sdk
assistant-runtime-provider
expo
hooks
index
index
index
introduction
migration
migration
overview
part-2
README
README
README
README
README
README
README
README
stockbroker
Cloud Message Pagination
index
Generative UI Date and Time Controls
In-Memory Thread Lifecycle
CHANGELOG
CHANGELOG
CHANGELOG
Local Runtime Message History
CHANGELOG
Markdown Preprocessing and Repair
CHANGELOG
CHANGELOG
MCP App Tool UI Rendering
CHANGELOG
CHANGELOG
message
MCP OAuth State Management
MessageRepository Branch Pointer Invariant
Next.js Cache Components
Remote Thread List Runtime
adapters
CHANGELOG
CHANGELOG
hooks
index
README
README
thread-runtime
Render Prop Leakage
Run Cancellation and Abort Signal Propagation
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
README
Run Telemetry Model ID Extraction
SSR-Safe Thread ID Generation
Stream Decoder Buffer Limits
CHANGELOG
CHANGELOG
StreamdownTextPrimitive
index
Streaming Markdown Rendering
CHANGELOG
CHANGELOG
index
introduction
part-1
AGENTS
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CODE_OF_CONDUCT
CONTRIBUTING
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README_LANGGRAPH
SECURITY
SPEC
SPEC_AssistantFrame
SPEC_ModelContextRegistry
action-bar
action-bar-more
adapters
artifacts
assistant-if
assistant-modal
assistant-modal
assistant-runtime
assistant-sidebar
attachment
attachment
attachment-runtime
branch-picker
chatgpt
claude
composer
composer-runtime
composition
context-display
custom-backend
custom-backend
error
file
form-demo
grok
heat-graph
hooks
image
index
index
index
index
index
index
index
index
index
markdown
mem0
mermaid
message-part
message-part-runtime
message-runtime
message-timing
modal
model-selector
part-3
perplexity
pick-a-runtime
primitives
primitives
quote
reasoning
selection-toolbar
sources
suggestion
syntax-highlighting
thread
thread
thread-list
thread-list
thread-list-item
thread-list-item-more
thread-list-item-runtime
thread-list-runtime
tool-fallback
tool-group
tw-shimmer
v4-legacy
v5-legacy