Building Real-Time Chat with React, SignalR, and Markdown Streaming
Enhancing Real-Time Interactions with Adaptive Technologies
Explore how to build a real-time React chat application integrated with SignalR for live messaging and Markdown for streaming content rendering. Learn step-by-step integration and how these technologies can enhance interactive user experiences.
Demonstration: Building Real-Time Chat Applications
In my ongoing journey to learn React and TypeScript, I have been enhancing the site I built. This post dives into integrating the site with my PromptSpark project by linking it to the existing SignalR implementation.
- Original Project Setup with Vite
Initially, I built my React site with Vite to benefit from fast development speeds, simplified setup, and compatibility with TypeScript. Starting with the basics, I created a functional and minimal site that allowed me to explore React's component-based structure, state management, and simple user interactions. Vite’s zero-config setup was a great advantage, allowing me to start coding immediately with hot-reloading and seamless builds.
- Integrating PromptSpark with SignalR
The goal with this update was to connect my site to PromptSpark, a tool I built to provide custom prompts for applications. I wanted a chat bot experience with PromptSpark varriants. To achieve this, I leveraged the existing SignalR hub created to facilitate two-way communication between users and the PromptSpark system. SignalR’s integration allows for real-time prompt suggestions and dynamic responses, creating a more interactive user experience.
- Reflections on Modular Development with React
By building on the foundation of my original site, I could focus on integrating these new features without needing a major overhaul. I took advantages of modular development with React and showcased the power of TypeScript and Vite for rapid development, even as requirements grow.
A complete guide to building a real-time chat app with TypeScript, SignalR, and Markdown rendering
- Setting Up the Project
-
I started with my Portfolio project using Vite for fast development builds, along with TypeScript and React. For this effor, I needed to add integration with SignalR for real-time messaging and `react-markdown` for rendering Markdown content.
npm install @microsoft/signalr react-markdown
- Connecting to SignalR for Real-Time Messaging
-
One of the first tasks was configuring SignalR to enable real-time, two-way communication between the server and the client. In my `Chat.tsx` file, I created a `HubConnection` to connect to the server and added an event listener to receive messages in real time.
import * as signalR from "@microsoft/signalr"; const connection = new signalR.HubConnectionBuilder() .withUrl("/chatHub") .build(); connection.on("ReceiveMessage", (user, message) => { setMessages((prev) => [...prev, { user, message }]); });
- Designing the Chat UI with React
-
The UI needed to be straightforward and mobile-responsive, so I used Bootstrap classes to quickly create the layout. I structured it with input fields for username and messages, a join button, and a display area for conversation-style messages.
return ( <div className="chat-container"> <input type="text" placeholder="Username" onChange={setUser} /> <input type="text" placeholder="Message" onChange={setMessage} /> <button onClick={sendMessage}>Join Chat</button> <div className="messages-display">{messages.map(...)</div> </div> );
- Integrating a Bot with Customizable Tones
-
I wanted the chat app to feature a bot with customizable tones, such as helpful, funny, or pirate-themed responses. I achieved this by passing a `variantName` prop to customize each bot’s tone, making it easy for users to choose a specific bot personality.
const BotResponse = ({ variantName }: { variantName: string }) => { switch(variantName) { case "pirate": return "Arr! Ye message received!"; case "helpful": return "How can I assist you today?"; // other variants } };
- Streaming Responses in Real Time
-
To achieve a real-time, streaming experience, I implemented logic to accumulate bot responses in chunks, displaying the content as it streamed in. Using a `streamingMessage` state, the bot’s responses would appear incrementally as data continued streaming from SignalR.
const [streamingMessage, setStreamingMessage] = useState(""); connection.on("StreamMessage", (chunk) => { setStreamingMessage((prev) => prev + chunk); });
- Rendering Markdown with react-markdown
-
For a richer text experience, I used `react-markdown` to render bot responses with Markdown formatting. This allowed the bot to send formatted messages, including bold text, lists, and links.
import ReactMarkdown from "react-markdown"; <ReactMarkdown>{markdownContent}</ReactMarkdown>;
- Handling Incremental Loading in the Chat
-
I added incremental message updates with the `FlatList` component's `ListFooterComponent`, allowing the UI to update as new Markdown chunks streamed in. This setup provided a seamless, real-time experience, with messages appearing as soon as they arrived.
<FlatList data={messages} renderItem={({ item }) => <ReactMarkdown>{item}</ReactMarkdown>} ListFooterComponent={<div>{streamingMessage}</div>} />
- Managing Errors and Fixing Styling Issues
Throughout development, I encountered TypeScript errors, especially with type mismatches in components. I solved these by refining types and ensuring all components aligned with TypeScript and React standards. Additionally, I removed redundant CSS by relying more on Bootstrap utilities for a cleaner, consistent style.
- Finalizing with Bootstrap Icons and Styles
For the final touches, I added Bootstrap Icons and styled components to keep the interface cohesive. This approach streamlined the code and enhanced user experience with clean, responsive visuals.
Conclusion
Building this chat app provided a deeper understanding of real-time communication, content streaming, and Markdown rendering in a React and TypeScript environment. It involved leveraging SignalR for bidirectional messaging, handling Markdown in real time, and creating dynamic bot personalities for a more engaging user experience.