"use client";
import {
Message,
MessageAction,
MessageActions,
MessageAttachment,
MessageAttachments,
MessageBranch,
MessageBranchContent,
MessageBranchNext,
MessageBranchPage,
MessageBranchPrevious,
MessageBranchSelector,
MessageContent,
MessageResponse,
MessageToolbar,
} from "@/components/ai-elements/message";
import {
CopyIcon,
RefreshCcwIcon,
ThumbsDownIcon,
ThumbsUpIcon,
} from "lucide-react";
import { nanoid } from "nanoid";
import { useState } from "react";
const messages: {
key: string;
from: "user" | "assistant";
versions?: { id: string; content: string }[];
content?: string;
attachments?: {
type: "file";
url: string;
mediaType?: string;
filename?: string;
}[];
}[] = [
{
key: nanoid(),
from: "user",
content: "How do React hooks work and when should I use them?",
attachments: [
{
type: "file",
url: "https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=400&fit=crop",
mediaType: "image/jpeg",
filename: "palace-of-fine-arts.jpg",
},
{
type: "file",
url: "",
mediaType: "application/pdf",
filename: "react-hooks-guide.pdf",
},
],
},
{
key: nanoid(),
from: "assistant",
versions: [
{
id: nanoid(),
content: `# React Hooks Guide
React hooks are functions that let you "hook into" React state and lifecycle features from function components. Here's what you need to know:
## Core Hooks
### useState
Adds state to functional components:
\`\`\`jsx
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
\`\`\`
### useEffect
Handles side effects (data fetching, subscriptions, DOM updates):
\`\`\`jsx
useEffect(() => {
document.title = \`You clicked \${count} times\`;
// Cleanup function (optional)
return () => {
document.title = 'React App';
};
}, [count]); // Dependency array
\`\`\`
## When to Use Hooks
- ✅ **Function components** - Hooks only work in function components
- ✅ **Replacing class components** - Modern React favors hooks over classes
- ✅ **Sharing stateful logic** - Create custom hooks to reuse logic
- ❌ **Class components** - Use lifecycle methods instead
## Rules of Hooks
1. Only call hooks at the **top level** (not inside loops, conditions, or nested functions)
2. Only call hooks from **React functions** (components or custom hooks)
Would you like to explore more advanced hooks like \`useCallback\` or \`useMemo\`?`,
},
{
id: nanoid(),
content: `React hooks are special functions that let you use React features in function components. The most common ones are:
- **useState** - for managing component state
- **useEffect** - for side effects like data fetching
- **useContext** - for consuming context values
- **useRef** - for accessing DOM elements
Here's a simple example:
\`\`\`jsx
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
}
\`\`\`
Which specific hook would you like to learn more about?`,
},
{
id: nanoid(),
content: `Absolutely! React hooks are a powerful feature introduced in React 16.8. They solve several problems:
## Key Benefits
1. **Simpler code** - No need for class components
2. **Reusable logic** - Extract stateful logic into custom hooks
3. **Better organization** - Group related code together
## Most Popular Hooks
| Hook | Purpose |
|------|---------|
| useState | Add state to components |
| useEffect | Handle side effects |
| useContext | Access context values |
| useReducer | Complex state logic |
| useCallback | Memoize functions |
| useMemo | Memoize values |
The beauty of hooks is that they let you reuse stateful logic without changing your component hierarchy. Want to dive into a specific hook?`,
},
],
},
];
const Example = () => {
const [liked, setLiked] = useState<Record<string, boolean>>({});
const [disliked, setDisliked] = useState<Record<string, boolean>>({});
const handleCopy = (content: string) => {
navigator.clipboard.writeText(content);
};
const handleRetry = () => {
console.log("Retrying...");
};
return (
<div className="flex flex-col gap-4">
{messages.map((message) => (
<Message from={message.from} key={message.key}>
{message.versions?.length && message.versions.length > 1 ? (
<MessageBranch defaultBranch={0} key={message.key}>
<MessageBranchContent>
{message.versions?.map((version) => (
<MessageContent key={version.id}>
<MessageResponse>{version.content}</MessageResponse>
</MessageContent>
))}
</MessageBranchContent>
{message.from === "assistant" && (
<MessageToolbar>
<MessageBranchSelector from={message.from}>
<MessageBranchPrevious />
<MessageBranchPage />
<MessageBranchNext />
</MessageBranchSelector>
<MessageActions>
<MessageAction
label="Retry"
onClick={handleRetry}
tooltip="Regenerate response"
>
<RefreshCcwIcon className="size-4" />
</MessageAction>
<MessageAction
label="Like"
onClick={() =>
setLiked((prev) => ({
...prev,
[message.key]: !prev[message.key],
}))
}
tooltip="Like this response"
>
<ThumbsUpIcon
className="size-4"
fill={liked[message.key] ? "currentColor" : "none"}
/>
</MessageAction>
<MessageAction
label="Dislike"
onClick={() =>
setDisliked((prev) => ({
...prev,
[message.key]: !prev[message.key],
}))
}
tooltip="Dislike this response"
>
<ThumbsDownIcon
className="size-4"
fill={disliked[message.key] ? "currentColor" : "none"}
/>
</MessageAction>
<MessageAction
label="Copy"
onClick={() =>
handleCopy(
message.versions?.find((v) => v.id)?.content || ""
)
}
tooltip="Copy to clipboard"
>
<CopyIcon className="size-4" />
</MessageAction>
</MessageActions>
</MessageToolbar>
)}
</MessageBranch>
) : (
<div key={message.key}>
{message.attachments && message.attachments.length > 0 && (
<MessageAttachments className="mb-2">
{message.attachments.map((attachment) => (
<MessageAttachment data={attachment} key={attachment.url} />
))}
</MessageAttachments>
)}
<MessageContent>
{message.from === "assistant" ? (
<MessageResponse>{message.content}</MessageResponse>
) : (
message.content
)}
</MessageContent>
{message.from === "assistant" && message.versions && (
<MessageActions>
<MessageAction
label="Retry"
onClick={handleRetry}
tooltip="Regenerate response"
>
<RefreshCcwIcon className="size-4" />
</MessageAction>
<MessageAction
label="Like"
onClick={() =>
setLiked((prev) => ({
...prev,
[message.key]: !prev[message.key],
}))
}
tooltip="Like this response"
>
<ThumbsUpIcon
className="size-4"
fill={liked[message.key] ? "currentColor" : "none"}
/>
</MessageAction>
<MessageAction
label="Dislike"
onClick={() =>
setDisliked((prev) => ({
...prev,
[message.key]: !prev[message.key],
}))
}
tooltip="Dislike this response"
>
<ThumbsDownIcon
className="size-4"
fill={disliked[message.key] ? "currentColor" : "none"}
/>
</MessageAction>
<MessageAction
label="Copy"
onClick={() => handleCopy(message.content || "")}
tooltip="Copy to clipboard"
>
<CopyIcon className="size-4" />
</MessageAction>
</MessageActions>
)}
</div>
)}
</Message>
))}
</div>
);
};
export default Example;