<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Uchit Chakma</title>
    <description>The latest articles on DEV Community by Uchit Chakma (@uchitchakma).</description>
    <link>https://dev.to/uchitchakma</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3965463%2F7375b188-851d-48ac-9b3f-76b08faf2d6f.jpg</url>
      <title>DEV Community: Uchit Chakma</title>
      <link>https://dev.to/uchitchakma</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/uchitchakma"/>
    <language>en</language>
    <item>
      <title>Building an AI Auto-Caption Tool for Videos with React + WebAssembly</title>
      <dc:creator>Uchit Chakma</dc:creator>
      <pubDate>Sun, 07 Jun 2026 00:39:05 +0000</pubDate>
      <link>https://dev.to/uchitchakma/building-an-ai-auto-caption-tool-for-videos-with-react-webassembly-b43</link>
      <guid>https://dev.to/uchitchakma/building-an-ai-auto-caption-tool-for-videos-with-react-webassembly-b43</guid>
      <description>&lt;h1&gt;
  
  
  Building an AI Auto-Caption Tool for Videos with React + WebAssembly
&lt;/h1&gt;

&lt;p&gt;Caption generation is one of the most in-demand features in video editing today. Social media creators need captions for Instagram Reels, TikTok, YouTube Shorts, and more. Traditional server-based solutions require expensive GPU infrastructure for transcription and rendering. But what if you could do everything in the browser?&lt;/p&gt;

&lt;p&gt;In this article, I will walk through the architecture of building a client-side auto-caption tool using React, WebAssembly, and modern browser APIs. The approach we used for &lt;a href="https://captionator.ucdreams.com/" rel="noopener noreferrer"&gt;Captionator&lt;/a&gt; keeps everything on the user's machine — no uploads, no server costs, and complete privacy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Client-Side Processing?
&lt;/h2&gt;

&lt;p&gt;Most video caption tools follow a server-based architecture. You upload a video, it gets processed on the backend, and you download the result. This works, but it has drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload times for large video files&lt;/li&gt;
&lt;li&gt;Server costs for GPU-backed transcription&lt;/li&gt;
&lt;li&gt;Privacy concerns when handling sensitive footage&lt;/li&gt;
&lt;li&gt;File size limitations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Modern browsers have evolved dramatically. With WebAssembly (Wasm), we can run computationally intensive tasks directly in the browser at near-native speed. Combined with the Web Audio API for audio extraction and Canvas API for video rendering, a fully client-side caption tool is not just possible — it is practical.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Architecture
&lt;/h2&gt;

&lt;p&gt;Our stack for Captionator looks like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend:&lt;/strong&gt; React with TypeScript&lt;br&gt;
&lt;strong&gt;Audio Processing:&lt;/strong&gt; Web Audio API + WebAssembly-ported Whisper model&lt;br&gt;
&lt;strong&gt;Video Rendering:&lt;/strong&gt; HTML5 Canvas + WebCodecs API&lt;br&gt;
&lt;strong&gt;Styling:&lt;/strong&gt; CSS-in-JS with custom animation engine&lt;/p&gt;

&lt;p&gt;The audio transcription step is the hardest part. Speech-to-text models are typically large and require GPU acceleration. However, with quantised Whisper models compiled to WebAssembly via ONNX Runtime Web, we can run inference directly in the browser.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1: Audio Extraction
&lt;/h2&gt;

&lt;p&gt;Before we can transcribe, we need raw audio from the video file. The Web Audio API makes this straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;audioContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AudioContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;videoFile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arrayBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;audioBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;audioContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decodeAudioData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us the raw PCM audio data, which we can feed into our transcription model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Running Transcription in the Browser
&lt;/h2&gt;

&lt;p&gt;For the transcription engine, we use a quantised version of OpenAI's Whisper model compiled to WebAssembly. ONNX Runtime Web handles the inference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;InferenceSession&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;onnxruntime-web&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;InferenceSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;whisperModelPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;feeds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Tensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;float32&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;audioData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dimensions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;feeds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key optimisation here is model quantisation. A full Whisper model is around 1.5GB. By quantising to 8-bit integers and trimming unnecessary layers, we get it down to about 300MB while maintaining over 95% accuracy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Word-Level Timing
&lt;/h2&gt;

&lt;p&gt;Raw transcription gives you text but not timing. For animated captions, we need word-level timestamps. This requires a forced alignment pass after the initial transcription.&lt;/p&gt;

&lt;p&gt;The Wav2Vec2 alignment model, also compiled to WebAssembly, handles this. It maps each word to its exact position in the audio timeline. This is what enables those smooth, perfectly synced caption animations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Rendering Captions onto Video
&lt;/h2&gt;

&lt;p&gt;This is where the magic happens. Using Canvas and WebCodecs, we decode the original video frame by frame and overlay captions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VideoDecoder&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;renderCaptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;captions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The caption renderer supports customisable styles — font, colour, animation, position. Each caption word gets its own animation properties: scale, opacity, and position offsets that create that viral "MrBeast-style" effect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Export Options
&lt;/h2&gt;

&lt;p&gt;Users need different output formats. We support:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;MP4 with baked-in captions&lt;/strong&gt; — using Canvas captureStream + MediaRecorder2. &lt;strong&gt;SRT file export&lt;/strong&gt; — for professional editing workflows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebVTT export&lt;/strong&gt; — for web video players&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The SRT export is particularly useful for YouTubers who want to upload subtitle files separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance Considerations
&lt;/h2&gt;

&lt;p&gt;Running Whisper in the browser is not trivial. Here are the key optimisations we made:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SIMD acceleration:&lt;/strong&gt; WebAssembly SIMD instructions speed up matrix operations by 3-4x&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-threading:&lt;/strong&gt; SharedArrayBuffer + Web Workers distribute the workload across CPU cores&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progressive processing:&lt;/strong&gt; Transcription starts as soon as enough audio is buffered — no need to wait for the full extract&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On a modern M-series Mac or a high-end Windows laptop, transcription completes in about 30% of the video duration. A 2-minute video transcribes in roughly 40 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  What About Mobile?
&lt;/h2&gt;

&lt;p&gt;Mobile browsers have less memory and slower CPUs. For mobile support, we fall back to a smaller distilled Whisper model (the "tiny" variant) that runs in under 100MB of RAM. Accuracy drops slightly but remains above 90%.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Business Case
&lt;/h2&gt;

&lt;p&gt;Building a client-side tool eliminates server costs entirely. For a startup or indie developer, this is massive. No GPU servers to rent, no bandwidth costs for video uploads, no storage for processed files.&lt;/p&gt;

&lt;p&gt;This is exactly why we built &lt;a href="https://captionator.ucdreams.com/" rel="noopener noreferrer"&gt;Captionator&lt;/a&gt; the way we did. It processes everything locally, stays free to use, and handles videos of any length since there are no upload limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Improvements
&lt;/h2&gt;

&lt;p&gt;We are exploring several enhancements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GPU acceleration via WebGPU&lt;/strong&gt; — for even faster transcription&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language detection&lt;/strong&gt; — auto-detect and transcribe 50+ languages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Emoji insertion&lt;/strong&gt; — automatically add emojis based on speech sentiment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch processing&lt;/strong&gt; — caption multiple videos in sequence&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Client-side video processing is not just a gimmick. It is a legitimate architecture choice that saves money, protects user privacy, and delivers a better experience. With modern WebAssembly, Canvas, and WebCodecs APIs, the browser has become a powerful media processing platform.&lt;/p&gt;

&lt;p&gt;If you are building a video tool, consider this approach. Your users will thank you for the speed and privacy. And your AWS bill will thank you for the zero server costs.&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://captionator.ucdreams.com/" rel="noopener noreferrer"&gt;Captionator&lt;/a&gt; to see the architecture in action. The entire tool is open for testing — no signup needed, just upload and go.&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>Building a Real-Time Chat Feature with Django Channels and React</title>
      <dc:creator>Uchit Chakma</dc:creator>
      <pubDate>Fri, 05 Jun 2026 00:10:07 +0000</pubDate>
      <link>https://dev.to/uchitchakma/building-a-real-time-chat-feature-with-django-channels-and-react-4e7e</link>
      <guid>https://dev.to/uchitchakma/building-a-real-time-chat-feature-with-django-channels-and-react-4e7e</guid>
      <description>&lt;h2&gt;
  
  
  Building a Real-Time Chat Feature with Django Channels and React
&lt;/h2&gt;

&lt;p&gt;Real-time features have become table stakes for modern web applications. Whether it is a customer support widget, a collaborative tool, or a social platform, users expect instant communication without page refreshes. In this article, I will walk through how we built a production-ready real-time chat feature using Django Channels and React at UCDREAMS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Django Channels?
&lt;/h3&gt;

&lt;p&gt;Django is traditionally synchronous. It handles one request at a time per worker. This works fine for standard HTTP requests, but WebSocket connections require persistent, bidirectional communication. Django Channels extends Django to handle WebSockets, background tasks, and asynchronous protocols alongside traditional HTTP.&lt;/p&gt;

&lt;p&gt;The beauty of Channels is that it does not replace Django. It layers on top, letting you keep your existing models, ORM, authentication, and admin panel while adding real-time capabilities. For a team already invested in Django, this is a massive advantage over introducing an entirely separate real-time server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up the Backend
&lt;/h3&gt;

&lt;p&gt;Start by installing Django Channels and a channel layer. Redis is the recommended backend for production use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;channels&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;channels&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mf"&gt;4.2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;daphne&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mf"&gt;4.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure your Django settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;channels&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;ASGI_APPLICATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_project.asgi.application&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;CHANNEL_LAYERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;BACKEND&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;channels_redis.core.RedisChannelLayer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CONFIG&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hosts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6379&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building the WebSocket Consumer
&lt;/h3&gt;

&lt;p&gt;The consumer handles WebSocket connections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;channels.generic.websocket&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncWebsocketConsumer&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChatConsumer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AsyncWebsocketConsumer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url_route&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kwargs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;room_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_group_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chat_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel_layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_group_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;close_code&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel_layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group_discard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_group_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text_data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;text_data_json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text_data_json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel_layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group_send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_group_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chat_message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chat_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The React Frontend
&lt;/h3&gt;

&lt;p&gt;On the React side, we use the native WebSocket API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Chat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setMessages&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setInput&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`ws://localhost:8000/ws/chat/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setMessages&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sendMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
      &lt;span class="nf"&gt;setInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"chat-container"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;:&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"input-area"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;onKeyPress&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Enter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;sendMessage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Send&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling Authentication
&lt;/h3&gt;

&lt;p&gt;In production, you will want authenticated WebSocket connections. Django Channels provides AuthMiddlewareStack which makes the user available in self.scope.user. For token-based auth with React, pass the token during connection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scaling Considerations
&lt;/h3&gt;

&lt;p&gt;For production deployment, consider these points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Redis Clustering - Use Redis Sentinel or Redis Cluster for high availability&lt;/li&gt;
&lt;li&gt;Daphne for Production - Run Daphne as your ASGI server&lt;/li&gt;
&lt;li&gt;Horizontal Scaling - Django Channels with Redis allows multiple app servers to share the channel layer&lt;/li&gt;
&lt;li&gt;Message Persistence - Store messages in your database for history&lt;/li&gt;
&lt;li&gt;Rate Limiting - Implement per-user rate limiting&lt;/li&gt;
&lt;li&gt;Connection Management - Handle reconnection with exponential backoff&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Real-World Implementation
&lt;/h3&gt;

&lt;p&gt;At UCDREAMS (&lt;a href="https://ucdreams.com" rel="noopener noreferrer"&gt;https://ucdreams.com&lt;/a&gt;), we have implemented this architecture for several client projects. One notable implementation powers a real-time customer support dashboard handling thousands of concurrent connections. The same pattern extends beyond chat to live notifications, collaborative editing, and real-time analytics.&lt;/p&gt;

&lt;p&gt;You can also check out Captionator (&lt;a href="https://captionator.ucdreams.com" rel="noopener noreferrer"&gt;https://captionator.ucdreams.com&lt;/a&gt;), a project where real-time WebSocket communication plays a key role in delivering captioning services with minimal latency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Django Channels plus React gives you a powerful, scalable foundation for real-time features. The learning curve is gentle if you already know Django and React, and the architecture scales from a simple chat room to complex real-time applications. For custom web application development tailored to your business needs, reach out to UCDREAMS.&lt;/p&gt;

</description>
      <category>django</category>
      <category>react</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>React + WordPress: The Full-Stack Combo That Powers Modern Web Applications</title>
      <dc:creator>Uchit Chakma</dc:creator>
      <pubDate>Thu, 04 Jun 2026 00:07:25 +0000</pubDate>
      <link>https://dev.to/uchitchakma/react-wordpress-the-full-stack-combo-that-powers-modern-web-applications-2570</link>
      <guid>https://dev.to/uchitchakma/react-wordpress-the-full-stack-combo-that-powers-modern-web-applications-2570</guid>
      <description>&lt;p&gt;If you think WordPress is just for blogs and React is just for single-page apps, you're missing the biggest trend in modern web development. Combining React with WordPress gives you the best of both worlds: a powerful CMS backend with a blazing-fast, component-driven frontend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;At &lt;a href="https://ucdreams.com" rel="noopener noreferrer"&gt;UCDREAMS&lt;/a&gt;, we've built everything from brochure sites to full-scale booking platforms using exactly this stack.&lt;/strong&gt; Here's why it works, and how you can use it too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why WordPress + React?
&lt;/h2&gt;

&lt;p&gt;WordPress powers over 40% of the web. Its REST API (and now the GraphQL API via WPGraphQL) makes it a headless CMS that can feed content to any frontend. React takes that content and turns it into a smooth, app-like experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The magic happens when you:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use WordPress as your content admin panel (clients love it)&lt;/li&gt;
&lt;li&gt;Expose content via REST or GraphQL APIs&lt;/li&gt;
&lt;li&gt;Build a React frontend that fetches and renders that content dynamically&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Setting Up the Stack
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: WordPress as a Headless CMS
&lt;/h3&gt;

&lt;p&gt;Install WordPress as usual. Add the &lt;strong&gt;WPGraphQL&lt;/strong&gt; plugin (or use the built-in REST API). Create custom post types with &lt;strong&gt;ACF (Advanced Custom Fields)&lt;/strong&gt; for structured content.&lt;/p&gt;

&lt;p&gt;For example, a portfolio site might have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom post type: &lt;code&gt;projects&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Fields: client name, tech stack, live URL, screenshots&lt;/li&gt;
&lt;li&gt;Categories: web, mobile, branding&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Build Your React Frontend
&lt;/h3&gt;

&lt;p&gt;Create a React app (Next.js is our preference at UCDREAMS for SSR/SEO benefits):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://yourdomain.com/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getProjects&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`{ projects { nodes { title slug acf { clientName techStack } } } }`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;API_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Deploy and Connect
&lt;/h3&gt;

&lt;p&gt;Deploy React on Vercel, Netlify, or your own server. Point your WordPress domain or subdomain to the React frontend. Use rewrites so &lt;code&gt;/about&lt;/code&gt; and &lt;code&gt;/contact&lt;/code&gt; still feel like a normal site.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Use Cases
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;E-commerce:&lt;/strong&gt; WooCommerce backend + React storefront = custom checkout flows, real-time inventory, and seamless UX.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Booking Platforms:&lt;/strong&gt; WordPress manages availability and bookings via plugins; React renders a calendar-based UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Portfolio/Agency Sites:&lt;/strong&gt; Clients edit content in WordPress; the live site is a lightning-fast React app.&lt;/p&gt;

&lt;p&gt;That's exactly the architecture we used at &lt;a href="https://ucdreams.com" rel="noopener noreferrer"&gt;UCDREAMS&lt;/a&gt; for several client projects. It gives non-technical clients full control over content while developers control the UI layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pros and Cons
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Clients use familiar WP admin&lt;/td&gt;
&lt;td&gt;Extra server for React rendering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fast, interactive frontend&lt;/td&gt;
&lt;td&gt;More complex deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SEO-friendly with Next.js&lt;/td&gt;
&lt;td&gt;Initial setup takes longer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scalable content structure&lt;/td&gt;
&lt;td&gt;Caching needs careful planning&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  When Should You NOT Use This Combo?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Simple 5-page brochure site? Just use vanilla WordPress.&lt;/li&gt;
&lt;li&gt;You have no developer on the team? Stick with page builders.&lt;/li&gt;
&lt;li&gt;Real-time features (chat, live updates)? Consider a full MERN stack.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But for &lt;strong&gt;mid-to-large content sites&lt;/strong&gt; that need a modern frontend experience? WordPress + React is the sweet spot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;If you're a developer, spin up a local WordPress instance, add WPGraphQL, and build a tiny React app that fetches posts. You'll see the potential in under an hour.&lt;/p&gt;

&lt;p&gt;If you need help building something production-ready, our team at &lt;strong&gt;&lt;a href="https://ucdreams.com" rel="noopener noreferrer"&gt;UCDREAMS&lt;/a&gt;&lt;/strong&gt; specializes in WordPress + React integrations, custom plugin development, and headless WordPress architecture. We're a Bangalore-based digital solutions agency with experience across e-commerce, booking platforms, and custom web applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's your experience been with headless WordPress? Drop a comment below!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>wordpress</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Hire a React.js Developer in Bangalore</title>
      <dc:creator>Uchit Chakma</dc:creator>
      <pubDate>Tue, 02 Jun 2026 23:52:21 +0000</pubDate>
      <link>https://dev.to/uchitchakma/how-to-hire-a-reactjs-developer-in-bangalore-pl1</link>
      <guid>https://dev.to/uchitchakma/how-to-hire-a-reactjs-developer-in-bangalore-pl1</guid>
      <description>&lt;h1&gt;
  
  
  How to Hire a React.js Developer in Bangalore: A Founder's Guide
&lt;/h1&gt;

&lt;p&gt;Finding the right React.js developer can make or break your project. In Bangalore, where the tech talent pool is deep but competitive, knowing how to identify, evaluate, and hire the right person is a skill in itself.&lt;/p&gt;

&lt;p&gt;I've hired React developers for my own agency at &lt;a href="https://ucdreams.com/services-development/" rel="noopener noreferrer"&gt;UCDREAMS&lt;/a&gt; and for client projects over the last three years. Here's what I've learned about finding great React talent in India's Silicon Valley.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Bangalore?
&lt;/h2&gt;

&lt;p&gt;Bangalore isn't just India's IT hub — it's the single largest concentration of React developers in the country. With over 8,000 tech companies, countless startups, and a thriving developer community, the city offers access to talent across every experience level and budget.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to Find React Developers in Bangalore
&lt;/h2&gt;

&lt;h3&gt;
  
  
  LinkedIn
&lt;/h3&gt;

&lt;p&gt;LinkedIn remains the most reliable platform. Use filters for "Bangalore" + "React.js" and look for candidates with 2+ years specific React experience, GitHub profiles, and community involvement.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub
&lt;/h3&gt;

&lt;p&gt;Bangalore has a vibrant open-source community. Search for React projects with contributors based in Bangalore.&lt;/p&gt;

&lt;h3&gt;
  
  
  Developer Communities
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;React Bangalore Meetup&lt;/strong&gt;: One of India's largest React communities&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dev.to and Hashnode&lt;/strong&gt;: Many Bangalore developers write here&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSConf India&lt;/strong&gt;: Great for networking with top talent&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What to Look For
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Technical Skills
&lt;/h3&gt;

&lt;p&gt;Beyond React itself, look for TypeScript, Next.js, state management (Redux, Zustand, React Query), testing (Jest, RTL), and modern styling (Tailwind, CSS-in-JS).&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Interview Questions
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;How does React's reconciliation algorithm work?&lt;/li&gt;
&lt;li&gt;How would you optimize component re-renders?&lt;/li&gt;
&lt;li&gt;What's the difference between controlled and uncontrolled components?&lt;/li&gt;
&lt;li&gt;How do you handle state in a large application?&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Salary Expectations in Bangalore (2026)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Junior&lt;/strong&gt; (0-2 yrs): ₹4-8 LPA&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mid-level&lt;/strong&gt; (2-5 yrs): ₹10-20 LPA&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Senior&lt;/strong&gt; (5+ yrs): ₹22-40 LPA&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architect/Lead&lt;/strong&gt;: ₹40-70 LPA&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hiring Models
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Full-time Employee
&lt;/h3&gt;

&lt;p&gt;Best for long-term projects needing deep product knowledge.&lt;/p&gt;

&lt;h3&gt;
  
  
  Freelancer / Contractor
&lt;/h3&gt;

&lt;p&gt;Ideal for specific projects with clear scope. Faster to hire, flexible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Development Agency
&lt;/h3&gt;

&lt;p&gt;When you need a team, not just an individual. Agencies like &lt;a href="https://ucdreams.com/services-development/" rel="noopener noreferrer"&gt;UCDREAMS&lt;/a&gt; provide end-to-end React development with project management, QA, and support.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hidden Cost of a Wrong Hire
&lt;/h2&gt;

&lt;p&gt;A bad hire costs more than salary — missed deadlines, technical debt, team morale, and lost opportunities. Start with a small paid project before committing full-time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Advice
&lt;/h2&gt;

&lt;p&gt;If you're a startup looking to build a React app without in-house hiring infrastructure, consider a &lt;a href="https://ucdreams.com/" rel="noopener noreferrer"&gt;professional web development agency&lt;/a&gt; that handles technical evaluation, scoping, and delivery.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This guide is based on experience at &lt;a href="https://ucdreams.com/" rel="noopener noreferrer"&gt;UCDREAMS&lt;/a&gt;, where we build React applications for startups and businesses across India, the UAE, and Australia.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
