Reality Check: Crafting a Chatbot UI with React and a Dash of Selling Sunset Glam (Part 2 of 2)
In our last blog, we dived into the world of chatbots with LangChain JS, but we were too busy weaving API magic and crafting message chains. Today, we're taking a detourโpicture this as a Selling Sunset-themed episodeโwhere the drama unfolds not in high-end real estate but within the pixels of our chatbot UI.
And guess what? The inspiration for this coding escapade came from some juicy chats with my friend Mary. We spilled the virtual tea on drama, showbiz, and everything in between. So, grab your coding shades because, inspired by the highs and lows of Selling Sunset, we're about to create a React masterpiece that's as captivating as the latest reality TV saga. Ready to turn your code into a star? Let's dive into the glitzy realm of frontend fun! ๐๐ฌ
Below is the result of what we're going to build:
- SantiHey there! Have you checked out the latest season of Selling Sunset? It's wild! ๐
- YouOh, totally! ๐ฑ The drama in that show is on another level. Can you believe the twists this season?
- SantiRight?! The real estate deals are insane, but I can't get enough of the office dynamics. Who's your favorite agent? ๐
- YouDefinitely Christine. She brings the drama and fashion game every time. ๐ But Amanza is a close second โ such a powerhouse!
- SantiAgreed! Christine's outfits are like a fashion show, and Amanza's work ethic is impressive. What do you think of the new listings they're showcasing? ๐ก
- YouThe houses are incredible! ๐ฐ I wouldn't mind living in one of those mansions. Did you see that $40 million one with the insane pool?
- SantiYeah, that pool was like a private water park! ๐ If only we could afford houses like that, right? Dream big, Santi!
- YouMaybe one day we'll be sipping champagne in our own Hollywood Hills mansion. ๐ฅ Can't wait for the next episode!
- SantiAbsolutely! See you at the virtual watch party next week?
Before building the UI, let's define the structure of our messages:
const messages = [{sender: "Santi",content:"Hey there! Have you checked out the latest season of Selling Sunset? It's wild! ๐",},{sender: "You",content:"Oh, totally! ๐ฑ The drama in that show is on another level.Can you believe the twists this season?",},...];
Now, let's build our UI with Tailwind. I won't explain in detail everything I did with Tailwind; I'll save that for another post. For now, the focus is on React. At this point, our component is static and has no interaction:
export default function Chat() {const messages = [{sender: "Santi",content:"Hey there! Have you checked out the latest season of Selling Sunset? It's wild! ๐",},{sender: "You",content:"Oh, totally! ๐ฑ The drama in that show is on another level.Can you believe the twists this season?",},...];return (<article className="p-6 chat"><div className="border border-gray-300 rounded-xl p-4 max-w-screen-md"><ul className="overflow-auto flex flex-col h-[350px] m-0 p-0">{messages.map((message, index) => (<likey={index}className={"grid grid-cols-12 pt-6"}><div className="col-span-1 flex justify-end">{message.sender === "Santi" ? (<Imageprioritysrc="/images/profile-pic.jpg"className="rounded-full h-6 w-6 border-[1px] border-slate-400"height={24}width={24}alt={message.sender}/>) : (<UserIcon />)}</div><div className="col-span-11 flex flex-col pl-4"><div className="font-semibold">{message.sender}</div><div className="font-light">{message.content}</div></div></li>))}</ul><form className="flex gap-4 items-center relative pt-4"><inputrequiredtype="text"name="message"placeholder="Message Santi..."className="border border-gray-300 p-2 rounded-xl flex-1 font-light"/><buttontype="submit"className="bg-gray-600 text-white font-bold rounded-xl h-8 w-8 p-1 absolute right-1 flex items-center justify-center"><div className="w-4 h-4"><SendIcon /></div></button></form></div></article>);}
This is how our component looks like this far:
- SantiHey there! Have you checked out the latest season of Selling Sunset? It's wild! ๐
- YouOh, totally! ๐ฑ The drama in that show is on another level. Can you believe the twists this season?
- SantiRight?! The real estate deals are insane, but I can't get enough of the office dynamics. Who's your favorite agent? ๐
- YouDefinitely Christine. She brings the drama and fashion game every time. ๐ But Amanza is a close second โ such a powerhouse!
- SantiAgreed! Christine's outfits are like a fashion show, and Amanza's work ethic is impressive. What do you think of the new listings they're showcasing? ๐ก
- YouThe houses are incredible! ๐ฐ I wouldn't mind living in one of those mansions. Did you see that $40 million one with the insane pool?
- SantiYeah, that pool was like a private water park! ๐ If only we could afford houses like that, right? Dream big, Santi!
- YouMaybe one day we'll be sipping champagne in our own Hollywood Hills mansion. ๐ฅ Can't wait for the next episode!
- SantiAbsolutely! See you at the virtual watch party next week?
What we're interested in now is having our component render each time there's a new message. For that, let's create a state variable using the useState
hook and create the handler to submit the form:
const [messages, setMessages] = useState([{sender: "Santi",content:"Hey there! Have you checked out the latest season of Selling Sunset? It's wild! ๐",},{sender: "You",content:"Oh, totally! ๐ฑ The drama in that show is on another level. Can you believe the twists this season?",},...]);...const handleSubmit = (event: React.FormEvent) => {event.preventDefault();const form = event.target as HTMLFormElement;const formData = new FormData(form);const newMessage = formData.get("message") as string;if (newMessage) {setMessages([...messages, { sender: "You", content: newMessage }]);form.reset();}};
Now, we want every time a new message is submitted, the element scrolls into view. To achieve this, we'll use element.scrollIntoView()
method. Let's start by adding a ref to the last element in our list of messages:
const lastMessageRef = useRef<HTMLLIElement>(null);...<ul className="overflow-auto flex flex-col max-h-[350px] m-0 p-0">{messages.map((message, index) => (<likey={index}// We add the ref to the last itemref={index === messages.length - 1 ? lastMessageRef : null}className={"grid grid-cols-12 pt-6"}>...</li>))}</ul>
We know that scrollIntoView()
has a side effect. As React won't update the ref of our last element until after updating the DOM, we won't use an event handler; we'll have to use the useEffect hook.
NOTE: If you wonder why we're not putting the
useEffect
argument with the dependency array, it's for two reasons: our ref is not a dependency, and we want it to scroll every time there's a render.
useEffect(() => {if (lastMessageRef.current) {lastMessageRef.current.scrollIntoView({behavior: 'smooth',block: 'nearest',inline: 'start'});}});
Ta-da! Our chatbot UI is ready for action! This is how the complete code looks. ๐ But hold the popcorn; we've got some blockbuster enhancements in mind:
- Word Unveiling Magic: Add animations that simulate words being typed, turning each message into a virtual cliffhanger.
- Silence the Empty Chatter: Disable the send button when the user forgets to spill the teaโno more accidental empty messages.
- Inclusive Glam: Sprinkle ARIA attributes for an inclusive and seamless chatbot experience.
Our chatbot might be drama-ready, but we're turning it into a blockbuster. Think of it as a coding reality showโeach enhancement reveals a new twist. Go on, code like it's a lifestyle, and let the chatbot saga continue! ๐ฌโจ