CONTENTS
- Introduction
- Prerequisities
- Project Setup
- Installing Dependencies
- Enviroment Configuration
- Project Structure
- Application Code
- Running & Testing
- Deployment
Introduction
This tutorial is to help you build a simple but modern ChatBot using Next.js. The chatbot provides an interface where the users can interact with an Ai powered model.
Prerequisties
Before you go straight into building in your IDE, you've got to have;
- Node.js installed(v-16.x or higher)
- Code IDE (Vscode recommended or any IDE of your choice)
- API Key (Would be using one from Groq)
- Basics on React and or Javascript
- Open terminal(admin) and nagivate to where you want to create your project.
- Next create your next.js project using
npx create-next-app@latest chatbot
- This prompt will follow up. Use the image below as reference for your prompt.
Installing Dependency
In terminal, typecd chatbot
to navigate to your project directory.
Now install groq sdk dependency using npm install --save groq-sdk
GROQ SETUP
- Visit GroqCloud and sign in to create an account.
- To get an API key you'd need to create one and remember to copy your key. Watch how to generate your key
- Back in your terminal, create a new file called ".env.local" in your project file.
touch .env.local
- Add your API key into the .env.local file.
echo "API_Key=your_copied_api_key" >> .env.local
- Also add your .env.local to .gitignore file in your project file if not added by default.
echo ".env.local" >> .gitignore
- Finally, type
code .
and hit enter to open your IDE(code editor).
Project Structure
Your project structure should look like this:
NB: "next.config.mjs", "jsconfig.json" and ".eslintrc.json" files are created automatically when you run npx create-next-app
Application Code
Here's a preview of what you'll be building.
- Open "app/layout.js" and edit it like this:
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "", //title of the site
description: "", //description of the site
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}
- In "app/global.css", you can modify it and add some styling like this:
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
@apply bg-gray-100 text-gray-900;
}
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background-color: rgba(60, 103, 109, 0.1);
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background-color: rgba(63, 198, 216, 0.2);
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background-color: rgba(60, 108, 114, 0.4);
}
- Lastly open "app/page.js" and write your code like this:
'use client';
import { useState, useEffect, FormEvent, useRef } from "react";
type Message = {
id: number;
sender: "user" | "bot";
text: string;
};
const ChatPage = () => {
const [messages, setMessages] = useState<Message[]>([]);
const [inputValue, setInputValue] = useState("");
const messagesEndRef = useRef<HTMLDivElement>(null);
const [loading, setLoading] = useState(false);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
};
useEffect(() => {
scrollToBottom();
}, [messages]);
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
if (!inputValue.trim()) return;
const userMessage: Message = {
id: Date.now(),
sender: "user",
text: inputValue.trim(),
};
setMessages((prevMessages) => [...prevMessages, userMessage]);
setInputValue("");
setLoading(true);
try {
const response = await fetch("/api/chat", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ message: userMessage.text }),
});
const data = await response.json();
if (response.ok) {
const botMessage: Message = {
id: Date.now() + 1,
sender: "bot",
text: data.message,
};
setMessages((prevMessages) => [...prevMessages, botMessage]);
} else {
const errorMessage: Message = {
id: Date.now() + 1,
sender: "bot",
text: data.error || "Processing failed",
};
setMessages((prevMessages) => [...prevMessages, errorMessage]);
}
} catch (error) {
console.error("Error fetching chat", error);
const errorMessage: Message = {
id: Date.now() + 1,
sender: "bot",
text: "Unexpected error occurred",
};
setMessages((prevMessages) => [...prevMessages, errorMessage]);
} finally {
setLoading(false);
}
};
return (
<div className="flex flex-col h-screen bg-blue-100">
{/* Header */}
<header className="bg-white shadow px-4 py-4">
<h1 className="font-semibold text-blue-800 text-2xl">
Chat with ElAioT
</h1>
</header>
{/* Chat Box */}
<div className="flex-1 p-4 overflow-y-auto">
{messages.map((message) => (
<div
key={message.id}
className={`flex flex-col ${message.sender === "user" ? "justify-end" : "justify-start"} mb-4`}
>
<div
className={`px-4 py-2 rounded-lg max-w-xl ${message.sender === "user" ? "bg-blue-500 text-white" : "bg-gray-200 text-gray-800"}`}
>
{message.text}
</div>
</div>
))}
{loading && (
<div className="flex justify-start mb-4">
<div className="flex space-x-1">
<span className="block w-2 h-2 bg-gray-400 rounded-full animate-pulse"></span>
<span className="block w-2 h-2 bg-gray-400 rounded-full animate-pulse delay-200"></span>
<span className="block w-2 h-2 bg-gray-400 rounded-full animate-pulse delay-400"></span>
</div>
</div>
)}
<div ref={messagesEndRef} />
</div>
{/* Input Form */}
<form onSubmit={handleSubmit} className="flex p-4 bg-white shadow">
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="Type your message..."
className="flex-1 border border-gray-300 rounded-full px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
disabled={loading}
/>
<button
type="submit"
className="ml-4 bg-blue-500 text-white p-2 rounded-full hover:bg-blue-600 focus:outline-none disabled:bg-blue-300"
disabled={loading}
>
{/* Send Icon (Paper Plane) */}
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M3 8L7.89 2.632a3 3 0 001.11 0L21 8M5 19h14a2 0 002-2v-7a2 2 0 00-2-2H5a2 2 0 00-2 2v7a2 2 002 2z"
/>
</svg>
</button>
</form>
</div>
);
};
export default ChatPage;
- Now, create "api/chat/route.ts" in "app" and write this:
import { NextResponse } from "next/server";
import Groq from "groq-sdk";
const groq = new Groq({ apiKey: process.env.GROQ_API_KEY });
export async function POST(request: Request) {
try {
const { message } = await request.json();
if (!message) {
return NextResponse.json(
{ error: "Message content is required" },
{ status: 400 }
);
}
const chatCompletion = await groq.chat.completions.create({
messages: [
{
role: "user",
content: message,
},
],
model: "llama-3.3-70b-versatile",
});
const responseMessage =
chatCompletion.choices[0]?.message?.content || "I'm sorry, I don't understand that.";
return NextResponse.json({ message: responseMessage });
} catch (error) {
console.error("Error in chat API", error);
return NextResponse.json(
{ error: "An error occurred while processing your request" },
{ status: 500 }
);
}
}
- Start the development server in your terminal using
npm run dev
- From the terminal, locate "http://localhost:3000", hold CTRL key and click on the link to open your build. (Ref from img in 1). An interface will show as seen in the preview.
- Play around in your build and when satisfied enough, press ` to stop the development server.
Deployment
For deployment, I'd be using vercel.
Vercel, a cloud platform which allows developer's host their projects.
- To begin, sign in with your GitHub to create an account on vercel. Follow the prompts, to get you started.
- Import your repository from GitHub through vercel
- Select your chatbot repository
- Scroll down to Environment variables and add your API_Key. (NB: This step is crucial. It allows vercel to know and use your API as done in your local dev server.)
- Click deploy and wait for vercel to do it thing.
- An interface like this would show:
- To access your deployed work, click on a link like this: "https://chatbot-pink-five.vercel.app/"
- In case you don't get step 2 to step 5, please use this link to watch a short video on it.
Congratulations your first chatbot has been deployed if you're able to get to this point.
I hope you find this documentation helpful in achieving your goals. Your success in following these guidelines is a testament to your dedication and skills.
If you found this documentation valuable, I encourage you to follow my future work for more insights, tips, and guidance. By doing so, you’ll not only stay updated but also help grow our community of like-minded individuals. Together, we can achieve even greater heights.
Thank you for your trust and collaboration.
Top comments (0)