Second Tool

In this section, we will add interactivity to your streamed components.

Update your ProductCarousel component to handle selection.

components/product-carousel.tsx
"use client"; 
/**
 * This code was generated by v0 by Vercel.
 * @see https://v0.dev/t/VFdYPYy7iUy
 * Documentation: https://v0.dev/docs#integrating-generated-code-into-your-nextjs-app
 */

import { Card } from "@/components/ui/card";
import {
  CarouselItem,
  CarouselContent,
  CarouselPrevious,
  CarouselNext,
  Carousel,
} from "@/components/ui/carousel";
import { useActions, useUIState } from "ai/rsc"; 
import { Button } from "./ui/button"; 
import { ClientMessage } from "@/app/actions"; 
import { nanoid } from "nanoid"; 
import { Product } from "@/lib/schemas/products";

export function ProductCarousel({
  products,
}: {
  products?: Product[];
}) {
  const [_, setConversation] = useUIState(); 
  const { continueConversation } = useActions(); 
  return (
    <Carousel className="w-full max-w-2xl">
      <CarouselContent>
        {products?.map((p) => (
          <CarouselItem key={p.name}>
            <Card className="flex flex-col items-center gap-4 p-6">
              <div className="text-center">
                <h3 className="text-xl font-semibold">{p.name}</h3>
                <p className="text-gray-500 dark:text-gray-400">
                  {p.description}
                </p>
                <Button
                  className="mt-4"
                  variant="outline"
                  onClick={async () => { 
                    console.log("Get product info for ", { product: p }); 
                    setConversation((currentConversation: ClientMessage[]) => [ 
                      ...currentConversation, 
                      { 
                        id: nanoid(), 
                        role: "user", 
                        display: "Get product info for " + p.name, 
                      }, 
                    ]);

                    const message = await continueConversation(
                      "Get product info for " + JSON.stringify({ product: p }),
                    );

                    setConversation((currentConversation: ClientMessage[]) => [
                      ...currentConversation,
                      message, 
                    ]);
                  }}
                >
                  More info
                </Button>
              </div>
            </Card>
          </CarouselItem>
        ))}
      </CarouselContent>
      <CarouselPrevious />
      <CarouselNext />
    </Carousel>
  );
}

Remember to mark it as a client component!

Let's generate a detailed product card component with v0. We can use this example and add it to your project, with the following command. Call it ProductCard.

npx v0 add NscAFoMGpdh -n ProductCard

Update the component to accept the product as a prop.

components/product-card.tsx
/**
 * This code was generated by v0 by Vercel.
 * @see https://v0.dev/t/NscAFoMGpdh
 * Documentation: https://v0.dev/docs#integrating-generated-code-into-your-nextjs-app
 */
import { Button } from "@/components/ui/button";
import { Product } from "@/lib/schemas/products"; 

export function ProductCard({
  product,
}: {
  product: Product;
}) {
  return (
    <div className="flex flex-col items-start gap-6 bg-white p-8 shadow-lg rounded-lg dark:bg-gray-950">
      <div className="space-y-2">
        <h2 className="text-2xl font-bold tracking-tight">{product.name}</h2>
        <p className="text-gray-500 dark:text-gray-400">
          {product.description}
        </p>
      </div>
      <div className="flex items-baseline gap-2">
        <span className="text-4xl font-bold">${product.price}</span>
        {product.type === "subscription" ? (
          <span className="text-gray-500 dark:text-gray-400 text-sm">
            /month
          </span>
        ) : null}
      </div>
      <Button className="w-full">
        {product.type === "subscription" ? "Subscribe" : "Buy"} Now
      </Button>
    </div>
  );
}

Add a new tool to handle getting a specific product.

app/actions.tsx
// REST OF CODE
    tools: {
      // your other tool
      getSpecificProduct: {
        description:
          "Select specific products. Use this if the user asks for more info on a product.",
        parameters: productSchema,
        generate: async function (product) {
          history.done((messages: ServerMessage[]) => [
            ...messages,
            {
              role: "assistant",
              content: "Selecting specific product:" + product.name,
            },
          ]);

          return (
            <ProductCard product={product} />
          );
        },
      }
    }
// REST OF CODE