Project Name: MindEase – AI-Powered Mental Health Chatbot
Description: Build a React-based web app featuring an AI chatbot that provides mental health support, stress-relief strategies, and guided meditation. The chatbot will use Vercel AI for real-time responses and will have a clean, modern UI focused on user well-being.
Key Features: ✅ AI Chatbot – Provides empathetic conversations, stress-relief tips, and meditation guidance. ✅ Guided Meditation – Offers breathing exercises and mindfulness sessions. ✅ Personalized Responses – Uses user input to tailor suggestions. ✅ Modern UI/UX – Minimalist, soft colors, and smooth animations for a calming experience. ✅ Secure & Private – No data storage, ensuring user confidentiality.
Tech Stack: 🔹 Frontend: React (Next.js for better performance), Tailwind CSS for styling , javascript 🔹 AI Integration: Vercel AI SDK for chatbot responses 🔹 Animations: Framer Motion for smooth UI interactions
UI Design Concept: 🟢 Soft, pastel color palette (blues & greens) for a calming effect 💬 Chatbot bubble UI with typing animation 🌙 Dark mode for nighttime users 🔄 Auto-suggested responses for easy interaction 🎧 Optional soothing background sounds during meditation
I'd be happy to help you build the MindEase AI-Powered Mental Health Chatbot using React, Next.js, and the Vercel AI SDK. Let's break this down into steps and create a basic implementation of the chatbot with some of the key features you've mentioned.
First, let's set up the project structure and implement the main chatbot functionality. Then, we'll add some of the UI elements and features you've described.
Let's start by creating the API route for the chatbot:
import { openai } from '@ai-sdk/openai';
import { streamText } from 'ai';
// Allow streaming responses up to 30 seconds
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4-turbo'),
system: "You are MindEase, an AI-powered mental health chatbot. Provide empathetic support, stress-relief strategies, and guided meditation instructions. Always prioritize user well-being and safety.",
messages,
});
return result.toDataStreamResponse();
}This API route sets up the chatbot using the Vercel AI SDK and OpenAI's GPT-4 model 12. It includes a system message to guide the AI's responses towards mental health support and meditation guidance.
Now, let's create the main chat component:
'use client'
import { useState, useEffect } from 'react'
import { useChat } from 'ai/react'
import { motion } from 'framer-motion'
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
import { Moon, Sun } from 'lucide-react'
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit } = useChat()
const [isTyping, setIsTyping] = useState(false)
const [darkMode, setDarkMode] = useState(false)
useEffect(() => {
if (darkMode) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
}, [darkMode])
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
setIsTyping(true)
handleSubmit(e).finally(() => setIsTyping(false))
}
return (
<div className={`flex items-center justify-center min-h-screen ${darkMode ? 'bg-gray-900' : 'bg-blue-50'}`}>
<Card className={`w-full max-w-2xl ${darkMode ? 'bg-gray-800 text-white' : 'bg-white'}`}>
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle>MindEase</CardTitle>
<Button
variant="ghost"
size="icon"
onClick={() => setDarkMode(!darkMode)}
aria-label="Toggle dark mode"
>
{darkMode ? <Sun className="h-6 w-6" /> : <Moon className="h-6 w-6" />}
</Button>
</CardHeader>
<CardContent className="h-[60vh] overflow-y-auto">
{messages.map(m => (
<motion.div
key={m.id}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
className={`mb-4 ${m.role === 'user' ? 'text-right' : 'text-left'}`}
>
<span className={`inline-block p-2 rounded-lg ${
m.role === 'user'
? 'bg-blue-500 text-white'
: darkMode
? 'bg-gray-700 text-white'
: 'bg-gray-200 text-black'
}`}>
{m.content}
</span>
</motion.div>
))}
{isTyping && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="text-left"
>
<span className={`inline-block p-2 rounded-lg ${
darkMode ? 'bg-gray-700 text-white' : 'bg-gray-200 text-black'
}`}>
MindEase is typing...
</span>
</motion.div>
)}
</CardContent>
<CardFooter>
<form onSubmit={onSubmit} className="flex w-full space-x-2">
<Input
value={input}
onChange={handleInputChange}
placeholder="Type your message..."
className={`flex-grow ${darkMode ? 'bg-gray-700 text-white' : ''}`}
/>
<Button type="submit" disabled={isTyping}>Send</Button>
</form>
</CardFooter>
</Card>
</div>
)
}This component sets up the chat interface using the useChat hook from the AI SDK 34. It includes the following features:
To complete the setup, you'll need to add the following to your tailwind.config.js:
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ['class'],
content: ["./pages/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}", "./app/**/*.{ts,tsx}", "./src/**/*.{ts,tsx}", "*.{js,ts,jsx,tsx,mdx}"],
theme: {
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
"accordion-down": {
from: { height: 0 },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
plugins: [require("tailwindcss-animate")],
}This configuration sets up the necessary styles for the shadcn/ui components and enables the dark mode functionality.
To further enhance the MindEase chatbot, you could consider adding the following features:
Remember to always prioritize user privacy and security, especially when dealing with sensitive mental health information. Ensure that no personal data is stored and that all conversations remain confidential.
This implementation provides a solid foundation for your MindEase AI-Powered Mental Health Chatbot. You can further customize the UI, add more features, and refine the AI responses to better suit your specific mental health support goals.
I'll create a more detailed interface for the MindEase AI-Powered Mental Health Chatbot, incorporating additional features you've mentioned. We'll use React with Next.js, Tailwind CSS for styling, and the Vercel AI SDK for the chatbot functionality.
Let's start by creating the main interface:
'use client'
import { useState, useEffect, useRef } from 'react'
import { useChat } from 'ai/react'
import { motion, AnimatePresence } from 'framer-motion'
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
import { Moon, Sun, Volume2, VolumeX, Play, Pause } from 'lucide-react'
import MoodTracker from '@/components/MoodTracker'
import GuidedMeditation from '@/components/GuidedMeditation'
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit } = useChat()
const [isTyping, setIsTyping] = useState(false)
const [darkMode, setDarkMode] = useState(false)
const [isSoundOn, setIsSoundOn] = useState(false)
const [showMoodTracker, setShowMoodTracker] = useState(false)
const [showMeditation, setShowMeditation] = useState(false)
const messagesEndRef = useRef<HTMLDivElement>(null)
useEffect(() => {
if (darkMode) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
}, [darkMode])
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
}, [messages])
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
setIsTyping(true)
handleSubmit(e).finally(() => setIsTyping(false))
}
const suggestedResponses = [
"I'm feeling anxious",
"How can I improve my sleep?",
"Tell me a relaxation technique"
]
return (
<div className={`flex items-center justify-center min-h-screen ${darkMode ? 'bg-gray-900' : 'bg-blue-50'}`}>
<Card className={`w-full max-w-2xl ${darkMode ? 'bg-gray-800 text-white' : 'bg-white'}`}>
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle className="text-2xl font-bold text-blue-600 dark:text-blue-400">MindEase</CardTitle>
<div className="flex space-x-2">
<Button
variant="ghost"
size="icon"
onClick={() => setIsSoundOn(!isSoundOn)}
aria-label="Toggle sound"
>
{isSoundOn ? <Volume2 className="h-6 w-6" /> : <VolumeX className="h-6 w-6" />}
</Button>
<Button
variant="ghost"
size="icon"
onClick={() => setDarkMode(!darkMode)}
aria-label="Toggle dark mode"
>
{darkMode ? <Sun className="h-6 w-6" /> : <Moon className="h-6 w-6" />}
</Button>
</div>
</CardHeader>
<CardContent className="h-[60vh] overflow-y-auto">
{messages.map(m => (
<motion.div
key={m.id}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
className={`mb-4 ${m.role === 'user' ? 'text-right' : 'text-left'}`}
>
<span className={`inline-block p-2 rounded-lg ${
m.role === 'user'
? 'bg-blue-500 text-white'
: darkMode
? 'bg-gray-700 text-white'
: 'bg-gray-200 text-black'
}`}>
{m.content}
</span>
</motion.div>
))}
{isTyping && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="text-left"
>
<span className={`inline-block p-2 rounded-lg ${
darkMode ? 'bg-gray-700 text-white' : 'bg-gray-200 text-black'
}`}>
MindEase is typing...
</span>
</motion.div>
)}
<div ref={messagesEndRef} />
</CardContent>
<CardFooter className="flex flex-col space-y-4">
<div className="flex space-x-2 w-full">
{suggestedResponses.map((response, index) => (
<Button
key={index}
variant="outline"
size="sm"
onClick={() => handleInputChange({ target: { value: response } } as React.ChangeEvent<HTMLInputElement>)}
>
{response}
</Button>
))}
</div>
<form onSubmit={onSubmit} className="flex w-full space-x-2">
<Input
value={input}
onChange={handleInputChange}
placeholder="Type your message..."
className={`flex-grow ${darkMode ? 'bg-gray-700 text-white' : ''}`}
/>
<Button type="submit" disabled={isTyping}>Send</Button>
</form>
<div className="flex justify-between w-full">
<Button variant="ghost" onClick={() => setShowMoodTracker(true)}>
Mood Tracker
</Button>
<Button variant="ghost" onClick={() => setShowMeditation(true)}>
Guided Meditation
</Button>
</div>
</CardFooter>
</Card>
<AnimatePresence>
{showMoodTracker && (
<MoodTracker onClose={() => setShowMoodTracker(false)} darkMode={darkMode} />
)}
{showMeditation && (
<GuidedMeditation onClose={() => setShowMeditation(false)} darkMode={darkMode} isSoundOn={isSoundOn} />
)}
</AnimatePresence>
</div>
)
}This main component sets up the chat interface using the useChat hook from the AI SDK 12. It includes the following features:
Now, let's create the MoodTracker component:
import React, { useState } from 'react'
import { motion } from 'framer-motion'
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
const moods = ['😊 Happy', '😐 Neutral', '😢 Sad', '😠 Angry', '😰 Anxious']
interface MoodTrackerProps {
onClose: () => void
darkMode: boolean
}
const MoodTracker: React.FC<MoodTrackerProps> = ({ onClose, darkMode }) => {
const [selectedMood, setSelectedMood] = useState<string | null>(null)
const handleMoodSelect = (mood: string) => {
setSelectedMood(mood)
// Here you could implement logic to save the mood data
setTimeout(onClose, 1000) // Close the tracker after a short delay
}
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center"
>
<Card className={`w-full max-w-md ${darkMode ? 'bg-gray-800 text-white' : 'bg-white'}`}>
<CardHeader>
<CardTitle>How are you feeling today?</CardTitle>
</CardHeader>
<CardContent className="grid grid-cols-3 gap-4">
{moods.map((mood) => (
<Button
key={mood}
onClick={() => handleMoodSelect(mood)}
variant={selectedMood === mood ? "default" : "outline"}
>
{mood}
</Button>
))}
</CardContent>
<CardFooter>
<Button onClick={onClose} variant="ghost">Close</Button>
</CardFooter>
</Card>
</motion.div>
)
}
export default MoodTrackerAnd the GuidedMeditation component:
import React, { useState, useEffect } from 'react'
import { motion } from 'framer-motion'
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
import { Play, Pause } from 'lucide-react'
interface GuidedMeditationProps {
onClose: () => void
darkMode: boolean
isSoundOn: boolean
}
const GuidedMeditation: React.FC<GuidedMeditationProps> = ({ onClose, darkMode, isSoundOn }) => {
const [isPlaying, setIsPlaying] = useState(false)
const [timeLeft, setTimeLeft] = useState(300) // 5 minutes in seconds
useEffect(() => {
let timer: NodeJS.Timeout
if (isPlaying && timeLeft > 0) {
timer = setInterval(() => {
setTimeLeft((prevTime) => prevTime - 1)
}, 1000)
}
return () => clearInterval(timer)
}, [isPlaying, timeLeft])
const togglePlay = () => {
setIsPlaying(!isPlaying)
if (isSoundOn) {
// Play or pause meditation audio here
}
}
const formatTime = (seconds: number) => {
const mins = Math.floor(seconds / 60)
const secs = seconds % 60
return `${mins}:${secs.toString().padStart(2, '0')}`
}
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center"
>
<Card className={`w-full max-w-md ${darkMode ? 'bg-gray-800 text-white' : 'bg-white'}`}>
<CardHeader>
<CardTitle>Guided Meditation</CardTitle>
</CardHeader>
<CardContent className="text-center">
<div className="text-4xl mb-4">{formatTime(timeLeft)}</div>
<Button onClick={togglePlay} size="lg">
{isPlaying ? <Pause className="mr-2" /> : <Play className="mr-2" />}
{isPlaying ? 'Pause' : 'Start'} Meditation
</Button>
</CardContent>
<CardFooter className="justify-between">
<Button onClick={onClose} variant="ghost">Close</Button>
{isSoundOn ? (
<span className="text-green-500">Sound On</span>
) : (
<span className="text-red-500">Sound Off</span>
)}
</CardFooter>
</Card>
</motion.div>
)
}
export default GuidedMeditationThese components create a more comprehensive interface for the MindEase chatbot, including:
To complete the setup, make sure you have the necessary dependencies installed:
npm install framer-motion lucide-react @radix-ui/react-slotThis implementation provides a solid foundation for your MindEase AI-Powered Mental Health Chatbot. The interface is clean, modern, and focused on user well-being, as per your requirements. It includes smooth animations for a calming experience and incorporates the key features you mentioned.
To further enhance the application, you could:
Remember to always prioritize user privacy and security, especially when dealing with sensitive mental health information. Ensure that no personal data is stored and that all conversations remain confidential.