[React AI Chatbot] Part 1: How To Build a React Chatbot UI?

Let's build a React chatbot UI with Vite, styled using Tailwind and Shadcn UI. Add Zustand for state management to handle messages, context, and a smooth UX.

How To Build a React Chatbot UI?

Chatbots are now a must-have for websites, powering customer support, sales, and user engagement. In 2025, users expect fast, personalized, and AI-driven conversations — and React JS is one of the best tools to deliver them.

With its component-based design, strong ecosystem, and flexibility, React is ideal to build interactive chatbot UIs and connect them to AI APIs like OpenAI GPT or Google Gemini.

There are 2 parts in the series, you'll learn how to:

  • Set up a modern React environment for chatbot development
  • Build a responsive chatbot UI with Tailwind CSS and Shadcn UI
  • Manage chat state and conversation context with Zustand
  • Create a secure Node.js backend with Express
  • Integrate with AI APIs (OpenAI GPT-4o and Google Gemini)
  • Implement real-time streaming responses
  • Add comprehensive error handling and user feedback
  • Deploy your chatbot to production
  • Implement advanced features like RAG and plugin integrations

By the end, you'll have a production-ready chatbot that can compete with commercial solutions.

>> Read more: Top 5 React AI Chatbot Templates You Should Know

In this first part, you’ll set up a fast React project with Vite, style it with Tailwind and Shadcn UI, and build a clean chat window that handles messages, context, and smooth UX. We’ll also add state management with Zustand.

Everything here happens on the frontend only. When you’re ready to connect real AI models, jump to Part 2: Node.js Chatbot AI for the backend, streaming, RAG, and deployment.

Prerequisites

Before diving into the tutorial, ensure you have everything ready:

  • Node.js (v20 or later) installed (Download here) and a package manager (npm/yarn/pnpm)
  • Git configured on your system
  • OpenAI API key obtained (Get your key)
  • Code editor with TypeScript support (VS Code recommended)
  • Basic React knowledge (components, hooks, props)
  • Terminal/command line familiarity
  • Internet connection for downloading dependencies
  • Modern web browser (Chrome, Firefox, Safari, Edge)

Quick verification:

# Check Node.js version
        node -v
        # Check npm version
        npm -v
        # Check Git installation
        git --version

Setup

Create a New Vite Project

Run the following commands in your terminal:=

# Create a new React + TypeScript project
        npm create vite@latest my-chatbot -- --template react-ts
        # Move into the project folder
        cd my-chatbot
        # Install dependencies
        npm install

This generates a lightweight React project with TypeScript ready to go.

In the past, many developers used Create React App (CRA) to scaffold projects. But by 2025, CRA is outdated because it’s slower, heavier, and less flexible. Being able to solve these issues, Vite has become the standard for React development.

Install Tailwind CSS

Tailwind will handle our styling, while Shadcn UI builds on top of it with polished, accessible React components.

npm install tailwindcss @tailwindcss/vite

Now replace everything in src/index.css with:

@import "tailwindcss";

Configure TypeScript Paths

Vite splits TypeScript configs into multiple files. I’ll add path aliases (@/) to make imports cleaner.

Update tsconfig.json and tsconfig.app.json with:

{
          "files": [],
          "references": [
            { "path": "./tsconfig.app.json" },
            { "path": "./tsconfig.node.json" }
        -  ]
        +  ],
        +  "compilerOptions": {
        +    "baseUrl": ".",
        +    "paths": {
        +      "@/*": ["./src/*"]
        +    }
        +  }
         }
     "noUnusedParameters": true,
             "erasableSyntaxOnly": true,
             "noFallthroughCasesInSwitch": true,
        -    "noUncheckedSideEffectImports": true
        +    "noUncheckedSideEffectImports": true,
        +
        +    "baseUrl": ".",
        +    "paths": {
        +      "@/*": ["./src/*"]
        +    }
           },
           "include": ["src"]
         }

Update Vite Configuration

Next, configure path resolution in vite.config.ts:

npm install -D @types/node
 import { defineConfig } from 'vite'
         import react from '@vitejs/plugin-react'
        +import tailwindcss from '@tailwindcss/vite';
        +import path from 'path';
         // https://vite.dev/config/
         export default defineConfig({
        -  plugins: [react()],
        -})
        +  plugins: [react(), tailwindcss()],
        +  resolve: {
        +    alias: {
        +      "@": path.resolve(__dirname, "./src"),
        +    },
        +  },
        +});

Initialize Shadcn UI

Finally, set up Shadcn UI, which will give us prebuilt, themeable components like buttons, inputs, and cards, perfect for our chatbot UI.

npx shadcn@latest init

You’ll be asked a few questions, like choosing a base color. For example:

Which color would you like to use as base color? › Neutral

Once setup is complete, you can start adding components:

npx shadcn@latest add button

Now you can import and use Shadcn components in your app:

import { Button } from "@/components/ui/button"
        export default function App() {
          return (
            <div className="flex min-h-screen items-center justify-center">
              <Button>Click me</Button>
            </div>
          )
        }

At this point, our project is set up with Vite, Tailwind CSS, and Shadcn UI, we can start building the actual chatbot interface. Use the following command to run up the project and access it at http://localhost:5173.

npm run dev

Build the React AI Chatbot UI

A good React chatbot UI should include:

  • A chat window to display the conversation.
  • Message bubbles styled differently for the user and the bot.
  • An input field + send button for user interaction.

I’ll build a reusable ChatWindow.tsx component that manages messages using React state and uses Shadcn UI components for styling.

Create a ChatWindow Component

Install these Shadcn components first.

npx shadcn@latest add card input

Create a file src/components/ChatWindow.tsx with the following code:

import React, { useState } from "react"
        import { Card, CardContent } from "@/components/ui/card"
        import { Input } from "@/components/ui/input"
        import { Button } from "@/components/ui/button"
        type Message = {
          role: "user" | "assistant"
          content: string
        }
        export default function ChatWindow() {
          const [messages, setMessages] = useState<Message[]>([
            { role: "assistant", content: "👋 Hi there! How can I help you today?" },
          ])
          const [input, setInput] = useState("")
          const sendMessage = () => {
            if (!input.trim()) return
            const newMessage: Message = { role: "user", content: input }
            setMessages([...messages, newMessage])
            setInput("")
            // Temporary bot reply (later we'll integrate AI)
            setTimeout(() => {
              setMessages((prev) => [
                ...prev,
                { role: "assistant", content: "Got it! I'm learning 🧠" },
              ])
            }, 800)
          }
          const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
            if (e.key === "Enter" && !e.shiftKey) {
              e.preventDefault()
              sendMessage()
            }
          }
          return (
            <Card className="w-full max-w-md mx-auto h-[600px] flex flex-col rounded-2xl shadow-lg">
              <CardContent className="flex-1 overflow-y-auto space-y-3 p-4">
                {messages.map((msg, i) => (
                  <div
                    key={i}
                    className={`p-3 rounded-xl max-w-[75%] ${
                      msg.role === "user"
                        ? "bg-blue-500 text-white ml-auto"
                        : "bg-gray-200 text-gray-800 mr-auto"
                    }`}
                  >
                    {msg.content}
                  </div>
                ))}
              </CardContent>
              <div className="flex items-center gap-2 border-t p-3">
                <Input
                  value={input}
                  onChange={(e) => setInput(e.target.value)}
                  onKeyDown={handleKeyDown}
                  placeholder="Type your message..."
                  className="flex-1"
                />
                <Button onClick={sendMessage}>Send</Button>
              </div>
            </Card>
          )
        }

Use It in App.tsx

Open src/App.tsx and update it:

import ChatWindow from "@/components/ChatWindow"
        export default function App() {
          return (
            <div className="flex min-h-screen items-center justify-center bg-gray-50">
              <ChatWindow />
            </div>
          )
        }

What’s Happening Here?

  • Messages State → I use useState to store all chat messages (both user and bot).
  • Dynamic Styling → Messages are styled differently depending on who sent them.
  • Modern UI → Shadcn UI components (Card, Input, Button) + Tailwind utilities handle the design.
  • Simulated Bot Reply → For now, the chatbot just echoes a dummy response. In the next section, I’ll replace this with real AI-powered responses.

You now have a fully working React chatbot UI where users can type messages, see them appear in a chat bubble, and get bot responses. Here is the result:

React Chatbot UI
React Chatbot UI

Manage Chatbot State and Add Conversation Context

To manage state in React, we can use either React Context or a lightweight state management library like Zustand. Zustand is a great choice because it’s minimal, easy to use, and works well for handling chat messages and user session data.

Install Zustand

Since our project only includes TailwindCSS and Shadcn so far, let’s install Zustand:

npm install zustand

Afterward, cancel the running application and restart it again.

Create a Chat Store with Zustand

Here’s a simple store to manage chatbot messages:

// src/store/chatStore.ts
    import { create } from "zustand";
    interface Message {
      id: string;
      sender: "user" | "assistant";
      text: string;
    }
    interface ChatState {
      messages: Message[];
      addMessage: (message: Message) => void;
      clearMessages: () => void;
    }
    export const useChatStore = create<ChatState>((set) => ({
      messages: [],
      addMessage: (message) =>
        set((state) => ({ messages: [...state.messages, message] })),
      clearMessages: () => set({ messages: [] }),
    }));

By using a store like this, your chatbot can:

  • Remember user details (like name, preferences, or session data).
  • Track ongoing conversations so replies are relevant.
  • Provide continuity instead of answering each prompt independently.

For example, if a user says “My name is Sarah”, your chatbot can store that and later reply with “Nice to see you again, Sarah!”. This makes your chatbot feel more human and helpful.

Without context management, your chatbot will act statelessly, responding to each message in isolation.

Extend the Chat Window with State and Context

In the last step, you created a global store using Zustand to manage messages and user details. Now, let’s connect that store to our chatbot UI and make the bot context-aware. This means the chatbot can “remember” details (like the user’s name) across the conversation — which is key when you want to add context to chatbot experiences.

Below is an enhanced ChatWindow.tsx component. It uses the useChatStore hook we built earlier, handles user input, and simulates bot replies. Notice how the bot can remember your name once you introduce yourself, and greet you personally in future messages.

import React, { useState } from "react";
    import { Card, CardContent } from "@/components/ui/card";
    import { Input } from "@/components/ui/input";
    import { Button } from "@/components/ui/button";
    import { useChatStore } from "@/store/chatStore";
    export default function ChatWindow() {
      const { messages, userName, addMessage, setUserName } = useChatStore();
      const [input, setInput] = useState("");
      const sendMessage = () => {
        if (!input.trim()) return;
        addMessage({ sender: "user", text: input });
        setInput("");
        setTimeout(() => {
          const botResponse = getBotResponse(input);
          addMessage({ sender: "assistant", text: botResponse });
        }, 800);
      };
      // Example bot reply (with context awareness)
      const getBotResponse = (userMessage: string) => {
        const message = userMessage.toLowerCase();
        if (message.includes("my name is")) {
          const name = userMessage.split("my name is")[1].trim();
          setUserName(name);
          return `Nice to meet you, ${name}! I'll remember your name.`;
        }
        if (message.includes("hello") || message.includes("hi")) {
          if (userName) {
            return `Hello ${userName}! How can I help you?`;
          }
          return "Hello! How can I help you?";
        }
        return "Thanks for your message! I'm a simple chatbot learning to respond.";
      };
      const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
        if (e.key === "Enter" && !e.shiftKey) {
          e.preventDefault();
          sendMessage();
        }
      };
      return (
        <Card className="w-full max-w-md mx-auto h-[600px] flex flex-col rounded-2xl shadow-lg">
          <CardContent className="flex-1 overflow-y-auto space-y-3 p-4">
            {messages.map((msg) => (
              <div
                key={msg.id}
                className={`p-3 rounded-xl max-w-[75%] ${
                  msg.sender === "user"
                    ? "bg-blue-500 text-white ml-auto"
                    : "bg-gray-200 text-gray-800 mr-auto"
                }`}
              >
                {msg.text}
              </div>
            ))}
          </CardContent>
          <div className="flex items-center gap-2 border-t p-3">
            <Input
              value={input}
              onChange={(e) => setInput(e.target.value)}
              onKeyDown={handleKeyDown}
              placeholder="Type your message..."
              className="flex-1"
            />
            <Button onClick={sendMessage}>Send</Button>
          </div>
        </Card>
      );
    }
    

And here is the result after implementing the above changes.

React Chatbot UI with Context Awareness

By combining Zustand for state management with a context-aware chatbot UI, you’ve built the foundation for a React chatbot UI that feels more natural and interactive. This approach scales easily — you can extend the bot to remember preferences, track conversation history, or even connect to an AI API.

Here’s a simple diagram showing how messages move between the user, the chatbot UI, and the global state store.

sequenceDiagram
    participant U as User
    participant UI as ChatWindow (React UI)
    participant S as Zustand Store
    participant B as Bot Logic

    U->>UI: Types message + presses Enter
    UI->>S: addMessage({ sender: "user", text })
    Note over S: Stores user message
    UI->>B: getBotResponse(userMessage)
    Note over B: Bot logic runs<br/>and may call setUserName()
    B->>S: setUserName(name) [if name detected]
    B->>S: addMessage({ sender: "bot", text })
    Note over S: Stores bot message<br/>and remembers userName
    S->>UI: Updated messages render automatically
    UI->>U: Displays conversation

Conclusion

You’ve now built the foundation of a modern React chatbot UI — fast, lightweight, and easy to extend. With Vite, Tailwind, and Shadcn UI, your interface loads instantly and looks professional out of the box. Through Zustand, your chatbot now remembers messages and context locally, giving users a smoother experience.

At this stage, your chatbot can display messages, manage state, and handle user interaction — all on the frontend.
In the next phase, we’ll bring it to life by connecting it to a real backend, streaming AI responses, and deploying it as a full-stack application.

Let's continue with the upcoming part: Node.js Chatbot AI to integrate your frontend with OpenAI or Gemini APIs and deliver real-time, intelligent conversations.

>>> Follow and Contact Relia Software for more information!

  • coding
  • Web application Development