suggestedActions component in vercel/ai-chatbot source code.
In this article, we will review what happens when you click on “What is the weather” widget on chat.vercel.ai. Let’s find out. Let’s start from this UI element “What is the weather”. I searched across the codebase for this string and I found this in components/suggested-actions. function PureSuggestedActions({ chatId, append }: SuggestedActionsProps) { const suggestedActions = [ { title: 'What are the advantages', label: 'of using Next.js?', action: 'What are the advantages of using Next.js?', }, { title: 'Write code to', label: `demonstrate djikstra's algorithm`, action: `Write code to demonstrate djikstra's algorithm`, }, { title: 'Help me write an essay', label: `about silicon valley`, action: `Help me write an essay about silicon valley`, }, { title: 'What is the weather', label: 'in San Francisco?', action: 'What is the weather in San Francisco?', }, ]; return ( {suggestedActions.map((suggestedAction, index) => ( 1 ? 'hidden sm:block' : 'block'} > { window.history.replaceState({}, '', `/chat/${chatId}`); append({ role: 'user', content: suggestedAction.action, }); }} className="text-left border rounded-xl px-4 py-3.5 text-sm flex-1 gap-1 sm:flex-col w-full h-auto justify-start items-start" > {suggestedAction.title} {suggestedAction.label} ))} ); } So what is happening on clicking a button here? onClick={async () => { window.history.replaceState({}, '', `/chat/${chatId}`); append({ role: 'user', content: suggestedAction.action, }); }} Two side-effects: window.history.replaceState append append here is a prop passed to PureSuggestedActions. interface SuggestedActionsProps { chatId: string; append: UseChatHelpers['append']; } function PureSuggestedActions({ chatId, append }: SuggestedActionsProps) { and this function memoized and assigned to SuggestedActions as shown below at the end of the file export const SuggestedActions = memo(PureSuggestedActions, () => true); Okay, so where is this SuggestedActions component is used then? well, it is found to be used in components/multimodal-input.tsx return ( {messages.length === 0 && attachments.length === 0 && uploadQueue.length === 0 && ( )} That makes sense, if you think about it, these suggestions only popup when there is no messages in the chat. The same pattern is found in the multimodal-inpiut, there is a memoized assignment as shown below. export const MultimodalInput = memo( PureMultimodalInput, (prevProps, nextProps) => { if (prevProps.input !== nextProps.input) return false; if (prevProps.status !== nextProps.status) return false; if (!equal(prevProps.attachments, nextProps.attachments)) return false; return true; }, ); and this is, in turn, used in components/chat.tsx const { messages, setMessages, handleSubmit, input, setInput, append, status, stop, reload, } = useChat({ id, body: { id, selectedChatModel: selectedChatModel }, initialMessages, experimental_throttle: 100, sendExtraMessageFields: true, generateId: generateUUID, onFinish: () => { mutate(unstable_serialize(getChatHistoryPaginationKey)); }, onError: () => { toast.error('An error occurred, please try again!'); }, }); useChat is imported as shown below: import { useChat } from '@ai-sdk/react'; Really, calling “append” on clicking that suggested action shows that how easy it is to integrate and build chat systems using Vercel APIs. About me: Hey, my name is Ramu Narasinga. I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos. I am open to work on interesting projects. Send me an email at ramu.narasinga@gmail.com My Github — https://github.com/ramu-narasinga My website — https://ramunarasinga.com My Youtube channel — https://www.youtube.com/@ramu-narasinga Learning platform — https://thinkthroo.com Codebase Architecture — https://app.thinkthroo.com/architecture Best practices — https://app.thinkthroo.com/best-practices Production-grade projects — https://app.thinkthroo.com/production-grade-projects References: https://github.com/vercel/ai-chatbot/blob/main/lib/ai/tools/get-weather.ts https://chat.vercel.ai/ https://github.com/vercel/ai-chatbot/blob/main/app/(chat)/api/chat/route.ts#L93 https://github.com/vercel/ai-chatbot/blob/main/components/multimodal-input.tsx#L271 https://github.com/vercel/ai-chatbot/blob/dfda9118d9291a9e21f91bf4eba7e80dcfd4bc49/components/s

In this article, we will review what happens when you click on “What is the weather” widget on chat.vercel.ai. Let’s find out.
Let’s start from this UI element “What is the weather”. I searched across the codebase for this string and I found this in components/suggested-actions.
function PureSuggestedActions({ chatId, append }: SuggestedActionsProps) {
const suggestedActions = [
{
title: 'What are the advantages',
label: 'of using Next.js?',
action: 'What are the advantages of using Next.js?',
},
{
title: 'Write code to',
label: `demonstrate djikstra's algorithm`,
action: `Write code to demonstrate djikstra's algorithm`,
},
{
title: 'Help me write an essay',
label: `about silicon valley`,
action: `Help me write an essay about silicon valley`,
},
{
title: 'What is the weather',
label: 'in San Francisco?',
action: 'What is the weather in San Francisco?',
},
];
return (
<div
data-testid="suggested-actions"
className="grid sm:grid-cols-2 gap-2 w-full"
>
{suggestedActions.map((suggestedAction, index) => (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 20 }}
transition={{ delay: 0.05 * index }}
key={`suggested-action-${suggestedAction.title}-${index}`}
className={index > 1 ? 'hidden sm:block' : 'block'}
>
<Button
variant="ghost"
onClick={async () => {
window.history.replaceState({}, '', `/chat/${chatId}`);
append({
role: 'user',
content: suggestedAction.action,
});
}}
className="text-left border rounded-xl px-4 py-3.5 text-sm flex-1 gap-1 sm:flex-col w-full h-auto justify-start items-start"
>
<span className="font-medium">{suggestedAction.title}</span>
<span className="text-muted-foreground">
{suggestedAction.label}
</span>
</Button>
</motion.div>
))}
</div>
);
}
So what is happening on clicking a button here?
onClick={async () => {
window.history.replaceState({}, '', `/chat/${chatId}`);
append({
role: 'user',
content: suggestedAction.action,
});
}}
Two side-effects:
window.history.replaceState
append
append here is a prop passed to PureSuggestedActions.
interface SuggestedActionsProps {
chatId: string;
append: UseChatHelpers['append'];
}
function PureSuggestedActions({ chatId, append }: SuggestedActionsProps) {
and this function memoized and assigned to SuggestedActions as shown below at the end of the file
export const SuggestedActions = memo(PureSuggestedActions, () => true);
Okay, so where is this SuggestedActions component is used then? well, it is found to be used in components/multimodal-input.tsx
return (
<div className="relative w-full flex flex-col gap-4">
{messages.length === 0 &&
attachments.length === 0 &&
uploadQueue.length === 0 && (
<SuggestedActions append={append} chatId={chatId} />
)}
<input
type="file"
className="fixed -top-4 -left-4 size-0.5 opacity-0 pointer-events-none"
ref={fileInputRef}
multiple
onChange={handleFileChange}
tabIndex={-1}
/>
That makes sense, if you think about it, these suggestions only popup when there is no messages in the chat.
The same pattern is found in the multimodal-inpiut, there is a memoized assignment as shown below.
export const MultimodalInput = memo(
PureMultimodalInput,
(prevProps, nextProps) => {
if (prevProps.input !== nextProps.input) return false;
if (prevProps.status !== nextProps.status) return false;
if (!equal(prevProps.attachments, nextProps.attachments)) return false;
return true;
},
);
and this is, in turn, used in components/chat.tsx
const {
messages,
setMessages,
handleSubmit,
input,
setInput,
append,
status,
stop,
reload,
} = useChat({
id,
body: { id, selectedChatModel: selectedChatModel },
initialMessages,
experimental_throttle: 100,
sendExtraMessageFields: true,
generateId: generateUUID,
onFinish: () => {
mutate(unstable_serialize(getChatHistoryPaginationKey));
},
onError: () => {
toast.error('An error occurred, please try again!');
},
});
useChat is imported as shown below:
import { useChat } from '@ai-sdk/react';
Really, calling “append” on clicking that suggested action shows that how easy it is to integrate and build chat systems using Vercel APIs.
About me:
Hey, my name is Ramu Narasinga. I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos.
I am open to work on interesting projects. Send me an email at ramu.narasinga@gmail.com
My Github — https://github.com/ramu-narasinga
My website — https://ramunarasinga.com
My Youtube channel — https://www.youtube.com/@ramu-narasinga
Learning platform — https://thinkthroo.com
Codebase Architecture — https://app.thinkthroo.com/architecture
Best practices — https://app.thinkthroo.com/best-practices
Production-grade projects — https://app.thinkthroo.com/production-grade-projects
References:
https://github.com/vercel/ai-chatbot/blob/main/lib/ai/tools/get-weather.ts
https://github.com/vercel/ai-chatbot/blob/main/app/(chat)/api/chat/route.ts#L93
https://github.com/vercel/ai-chatbot/blob/main/components/multimodal-input.tsx#L271
https://github.com/vercel/ai-chatbot/blob/main/components/chat.tsx#L90