Second Tool
In this section, we will add interactivity to your streamed components.
Update your ProductCarousel
component to handle selection.
"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.
/**
* 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.
// 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