<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Akinwumi Iyanuoluwa Ayomiposi - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Akinwumi Iyanuoluwa Ayomiposi - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 19:47:54 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/steelthedev/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Real-Time Chat App With Go, Fiber and HTMX ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, you'll build a simple real-time chat app using Go, Fiber and HTMX.  You will learn how to leverage the versatility of Fiber by making use of a WebSocket. You'll also learn how to create a reactive frontend without the use of JavaScr... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/real-time-chat-with-go-fiber-htmx/</link>
                <guid isPermaLink="false">66b90552e2f97a1f15f5f769</guid>
                
                    <category>
                        <![CDATA[ Chat ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Akinwumi Iyanuoluwa Ayomiposi ]]>
                </dc:creator>
                <pubDate>Thu, 06 Jun 2024 18:06:35 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/06/websocket.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, you'll build a simple real-time chat app using Go, Fiber and HTMX. </p>
<p>You will learn how to leverage the versatility of Fiber by making use of a WebSocket. You'll also learn how to create a reactive frontend without the use of JavaScript.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li>A good understanding of Go and HTTP servers.</li>
<li>Go must be installed (Go version 1.22 will be used in this project).</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-getting-started">Getting Started</a></li>
<li><a class="post-section-overview" href="#heading-how-to-install-dependencies">How to Install Dependencies</a></li>
<li><a class="post-section-overview" href="#heading-maingo-file">main.go File</a></li>
<li><a class="post-section-overview" href="#heading-static-files">Static Files</a></li>
<li><a class="post-section-overview" href="#heading-how-to-configure-static-files">How to Configure Static Files</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-handlers">How to Create Handlers</a></li>
<li><a class="post-section-overview" href="#heading-messagesgo-file">messages.go File</a></li>
<li><a class="post-section-overview" href="#heading-websocketgo-file">websocket.go File</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-a-websocket-to-routes-and-htmx">How to Add a WebSocket to Routes and HTMX</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a>  </li>
</ul>
<h2 id="heading-getting-started">Getting Started</h2>
<p>First, create a new folder named <strong>go-chat</strong>. Initiate Go in your project by running <code>go mod init pakacage_name</code>, as can seen below:</p>
<pre><code>go mod init github.com/steelthedev/go-chat
</code></pre><h3 id="heading-how-to-install-dependencies">How to Install Dependencies</h3>
<p>You need to install some libraries which are very vital. This can be done in the terminal by running the following commands:</p>
<pre><code>go get -u github.com/gofiber/fiber/v2
go get -u github.com/gofiber/websocket/v2
go get -u github.com/gofiber/template/html/v2
</code></pre><p>These will install Fiber and other components such as WebSocket and HTML templating library.</p>
<h3 id="heading-maingo-file">main.go File</h3>
<p>In the root directory, create a <strong>main.go</strong> file. This file will be the entry point to the application. Inside the file we are going to create a simple web server:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"github.com/gofiber/fiber/v2"</span>


<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {

    <span class="hljs-comment">// Start new fiber instance</span>
    app := fiber.New()

    <span class="hljs-comment">// Create a "ping" handler to test the server</span>
    app.Get(<span class="hljs-string">"/ping"</span>, <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(ctx *fiber.Ctx)</span> <span class="hljs-title">error</span></span>{
        <span class="hljs-keyword">return</span> ctx.SendString(<span class="hljs-string">"Welcome to fiber"</span>)
    })

    <span class="hljs-comment">// Start the http server</span>
    app.Listen(<span class="hljs-string">":3000"</span>)
}
</code></pre>
<p>Save the file and run <code>go run main.go</code> in the terminal to start the web server. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/tuts1-cropped.png" alt="Image" width="600" height="400" loading="lazy">
<em><code>go run</code> command starting the web server</em></p>
<p>If you head over to the browser and test the <code>/ping</code> route, there should be a response like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/welcome-to-fiber-cropped.png" alt="Image" width="600" height="400" loading="lazy">
<em>/ping route in the browser</em></p>
<h2 id="heading-static-files">Static Files</h2>
<p>We'll need static files such as CSS and HTML files for the application to function. Create two folders with named <strong>static</strong> and <strong>views</strong>. Inside the <strong>views</strong> folder, create two html files: <strong>index.html</strong> and <strong>messages.html</strong>.</p>
<p>Here's what the <strong>index.html</strong> file should look like:</p>
<pre><code class="lang-go">&lt;!DOCTYPE html&gt;
&lt;html lang=<span class="hljs-string">"en"</span>&gt;

&lt;head&gt;
    &lt;meta charset=<span class="hljs-string">"UTF-8"</span>&gt;
    &lt;meta name=<span class="hljs-string">"viewport"</span> content=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;
    &lt;title&gt;Chat Room&lt;/title&gt;
    &lt;script src=<span class="hljs-string">"https://unpkg.com/htmx.org@1.9.10"</span>
        integrity=<span class="hljs-string">"sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC"</span>
        crossorigin=<span class="hljs-string">"anonymous"</span>&gt;&lt;/script&gt;
    &lt;!-- HTMX Websockets extension https:<span class="hljs-comment">//htmx.org/extensions/web-sockets/ --&gt;</span>
    &lt;script src=<span class="hljs-string">"https://unpkg.com/htmx.org/dist/ext/ws.js"</span>&gt;&lt;/script&gt;
    &lt;link href=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"</span> rel=<span class="hljs-string">"stylesheet"</span>
        integrity=<span class="hljs-string">"sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"</span> crossorigin=<span class="hljs-string">"anonymous"</span>&gt;
    &lt;link rel=<span class="hljs-string">"stylesheet"</span> href=<span class="hljs-string">"/static/style.css"</span>&gt;
&lt;/head&gt;

&lt;body&gt;
    &lt;div class=<span class="hljs-string">"container"</span>&gt;
        &lt;div class=<span class="hljs-string">"chat-window"</span>&gt;
            &lt;div class=<span class="hljs-string">"messages"</span> id=<span class="hljs-string">"messages"</span> &gt;
                &lt;!-- Messages will be appended here --&gt;
            &lt;/div&gt;
            &lt;form id=<span class="hljs-string">"form"</span>&gt;
                &lt;div class=<span class="hljs-string">"input-area"</span>&gt;
                    &lt;input <span class="hljs-keyword">type</span>=<span class="hljs-string">"text"</span> name=<span class="hljs-string">"text"</span> min=<span class="hljs-string">"1"</span> id=<span class="hljs-string">"messageInput"</span> placeholder=<span class="hljs-string">"Type a message..."</span>&gt;
                    &lt;button <span class="hljs-keyword">type</span>=<span class="hljs-string">"submit"</span>&gt;Send&lt;/button&gt;
                &lt;/div&gt;
            &lt;/form&gt;

        &lt;/div&gt;
    &lt;/div&gt;

&lt;/body&gt;

&lt;/html&gt;
</code></pre>
<p>In the <strong>index.html</strong> above, we have linked the necessary plugins such as our <strong>style.css</strong> which will soon be created, HTMX and bootstrap 5.</p>
<p>Here's what the <strong>message.html</strong> file should look like:</p>
<pre><code>&lt;div id=<span class="hljs-string">"messages"</span> hx-swap-oob=<span class="hljs-string">"beforeend"</span>&gt;
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-small"</span>&gt;</span>{{ .Text }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
&lt;/div&gt;
</code></pre><p>This message will be the response from the server, it will be swapped into our <strong>index.html</strong> code automatically in the browser with the help of HTMX.</p>
<p>Now, create a new folder named <strong>static</strong>. Inside it, create a new file <strong>style.css</strong>:</p>
<pre><code>body {
    <span class="hljs-attr">margin</span>: <span class="hljs-number">0</span>;
    padding: <span class="hljs-number">0</span>;
    font-family: Arial, sans-serif;
    background-color: #f2f2f2;
}

.container {
    <span class="hljs-attr">display</span>: flex;
    justify-content: center;
    align-items: center;
    height: <span class="hljs-number">100</span>vh;
}

.chat-<span class="hljs-built_in">window</span> {
    <span class="hljs-attr">width</span>: <span class="hljs-number">400</span>px;
    background-color: #fff;
    border-radius: <span class="hljs-number">10</span>px;
    box-shadow: <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">10</span>px rgba(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.1</span>);
}

.messages {
    <span class="hljs-attr">padding</span>: <span class="hljs-number">10</span>px;
    overflow-y: scroll;
    height: <span class="hljs-number">300</span>px;
}

.message {
    margin-bottom: <span class="hljs-number">10</span>px;
}

.message p {
    background-color: #f0f0f0;
    border-radius: <span class="hljs-number">5</span>px;
    padding: <span class="hljs-number">5</span>px <span class="hljs-number">10</span>px;
    display: inline-block;
    max-width: <span class="hljs-number">80</span>%;
}

.input-area {
    <span class="hljs-attr">padding</span>: <span class="hljs-number">10</span>px;
    display: flex;
}

.input-area input[type=<span class="hljs-string">"text"</span>] {
    <span class="hljs-attr">flex</span>: <span class="hljs-number">1</span>;
    padding: <span class="hljs-number">8</span>px;
    border: <span class="hljs-number">1</span>px solid #ccc;
    border-radius: <span class="hljs-number">5</span>px;
    margin-right: <span class="hljs-number">5</span>px;
}

.input-area button {
    <span class="hljs-attr">padding</span>: <span class="hljs-number">8</span>px <span class="hljs-number">15</span>px;
    background-color: #<span class="hljs-number">4</span>CAF50;
    color: white;
    border: none;
    border-radius: <span class="hljs-number">5</span>px;
    cursor: pointer;
    transition: background-color <span class="hljs-number">0.3</span>s;
}

.input-area button:hover {
    background-color: #<span class="hljs-number">45</span>a049;
}

.input-area button:active {
    background-color: #<span class="hljs-number">3e8</span>e41;
}
</code></pre><h2 id="heading-how-to-configure-static-files">How to Configure Static Files</h2>
<p>In your <strong>main.g</strong>o file, you need to tell Fiber how to handle your static files, most especially the folder to check for HTML rendering. Update <strong>main.go</strong> as follow:</p>
<pre><code>package main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"github.com/gofiber/fiber/v2"</span>
    <span class="hljs-string">"github.com/gofiber/template/html/v2"</span>
)

func main() {


    <span class="hljs-comment">// Create views engine</span>
    <span class="hljs-attr">viewsEngine</span> := html.New(<span class="hljs-string">"./views"</span>, <span class="hljs-string">".html"</span>)


    <span class="hljs-comment">// Start new fiber instance</span>
    <span class="hljs-attr">app</span> := fiber.New(fiber.Config{
        <span class="hljs-attr">Views</span>: viewsEngine,
    })

    <span class="hljs-comment">// Static route and directory</span>
    app.Static(<span class="hljs-string">"/static/"</span>, <span class="hljs-string">"./static"</span>)

    <span class="hljs-comment">// Create a "ping" handler to test the server</span>
    app.Get(<span class="hljs-string">"/ping"</span>, func(ctx *fiber.Ctx) error{
        <span class="hljs-keyword">return</span> ctx.SendString(<span class="hljs-string">"Welcome to fiber"</span>)
    })

    <span class="hljs-comment">// Start the http server</span>
    app.Listen(<span class="hljs-string">":3000"</span>)

}
</code></pre><p>As seen above, a configuration was added to the app instance and also configured the static route to be <code>/static/</code>.</p>
<h2 id="heading-how-to-create-handlers">How to Create Handlers</h2>
<p>Create a new file named <strong>handlers.go</strong>:</p>
<pre><code>package handlers

<span class="hljs-keyword">import</span> <span class="hljs-string">"github.com/gofiber/fiber/v2"</span>

type AppHandler struct{}

func NewAppHandler() *AppHandler {
    <span class="hljs-keyword">return</span> &amp;AppHandler{}
}

func (a *AppHandler) HandleGetIndex(ctx *fiber.Ctx) error {
    <span class="hljs-attr">context</span> := fiber.Map{}
    <span class="hljs-keyword">return</span> ctx.Render(<span class="hljs-string">"index"</span>, context)
}
</code></pre><p>In the code above, we created a handler which received the <code>AppHandler</code> struct. This helps with abstractions in case the code gets bigger. The <code>HandleGetIndex</code> function takes in a pointer to the Fiber context and renders the <strong>index.html</strong> file.</p>
<p>In <strong>main.go</strong>:</p>
<pre><code>package main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"github.com/gofiber/fiber/v2"</span>
    <span class="hljs-string">"github.com/gofiber/template/html/v2"</span>
    <span class="hljs-string">"github.com/steelthedev/go-chat/handlers"</span>
)

func main() {

    <span class="hljs-comment">// Start new fiber instance</span>
    <span class="hljs-attr">app</span> := fiber.New()

    <span class="hljs-comment">// Create a "ping" handler to test the server</span>
    app.Get(<span class="hljs-string">"/ping"</span>, func(ctx *fiber.Ctx) error{
        <span class="hljs-keyword">return</span> ctx.SendString(<span class="hljs-string">"Welcome to fiber"</span>)
    })

    <span class="hljs-comment">// create new App Handler</span>
    <span class="hljs-attr">appHandler</span> := NewAppHandler()

    <span class="hljs-comment">// Add appHandler routes</span>
    app.Get(<span class="hljs-string">"/, appHandler.HandleGetIndex)

    // Start the http server
    app.Listen("</span>:<span class="hljs-number">3000</span><span class="hljs-string">")
}</span>
</code></pre><p>Above, we created a new app handler and added the <code>HandleGetIndex</code> function in the routes. Run the <code>go run main.go</code> command. On <code>localhost:3000</code>, you should have a screen similar to this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/chat-room-cropped.png" alt="Image" width="600" height="400" loading="lazy">
<em>input box, send button, and chat display area on localhost:3000</em></p>
<h3 id="heading-messagesgo-file">messages.go File</h3>
<p>Create a new file in the project directly and name it <strong>message.go</strong>. This file will host the message struct.</p>
<pre><code>package main

type Message struct {
    Text       string <span class="hljs-string">`json:"text"`</span>
}
</code></pre><h3 id="heading-websocketgo-file">websocket.go File</h3>
<p>Create a new file in the project directory and name it <strong>websocket.go</strong>. This will house the main function creating the WebSocket server, reading through it and writing to all channels:</p>
<pre><code>package main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"bytes"</span>
    <span class="hljs-string">"encoding/json"</span>
    <span class="hljs-string">"html/template"</span>
    <span class="hljs-string">"log"</span>

    <span class="hljs-string">"github.com/gofiber/fiber/v2"</span>
    <span class="hljs-string">"github.com/gofiber/websocket/v2"</span>
)

type WebSocketServer struct {
    clients   map[*websocket.Conn]bool
    broadcast chan *Message
}

func NewWebSocket() *WebSocketServer {
    <span class="hljs-keyword">return</span> &amp;WebSocketServer{
        <span class="hljs-attr">clients</span>:   make(map[*websocket.Conn]bool),
        <span class="hljs-attr">broadcast</span>: make(chan *Message),
    }
}


func (s *WebSocketServer) HandleWebSocket(ctx *websocket.Conn) {

    <span class="hljs-comment">// Register a new Client</span>
    s.clients[ctx] = <span class="hljs-literal">true</span>
    defer func() {
        <span class="hljs-keyword">delete</span>(s.clients, ctx)
        ctx.Close()
    }()

    <span class="hljs-keyword">for</span> {
        _, msg, <span class="hljs-attr">err</span> := ctx.ReadMessage()
        <span class="hljs-keyword">if</span> err != nil {
            log.Println(<span class="hljs-string">"Read Error:"</span>, err)
            <span class="hljs-keyword">break</span>
        }

        <span class="hljs-comment">// send the message to the broadcast channel</span>
        <span class="hljs-keyword">var</span> message Message
        <span class="hljs-keyword">if</span> err := json.Unmarshal(msg, &amp;message); err != nil {
            log.Fatalf(<span class="hljs-string">"Error Unmarshalling"</span>)
        }
        s.broadcast &lt;- &amp;message
    }
}

func (s *WebSocketServer) HandleMessages() {
    <span class="hljs-keyword">for</span> {
        <span class="hljs-attr">msg</span> := <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">-s.broadcast</span>

        // <span class="hljs-attr">Send</span> <span class="hljs-attr">the</span> <span class="hljs-attr">message</span> <span class="hljs-attr">to</span> <span class="hljs-attr">all</span> <span class="hljs-attr">Clients</span>

        <span class="hljs-attr">for</span> <span class="hljs-attr">client</span> <span class="hljs-attr">:</span>= <span class="hljs-string">range</span> <span class="hljs-attr">s.clients</span> {
            <span class="hljs-attr">err</span> <span class="hljs-attr">:</span>= <span class="hljs-string">client.WriteMessage(websocket.TextMessage,</span> <span class="hljs-attr">getMessageTemplate</span>(<span class="hljs-attr">msg</span>))
            <span class="hljs-attr">if</span> <span class="hljs-attr">err</span> != <span class="hljs-string">nil</span> {
                <span class="hljs-attr">log.Printf</span>("<span class="hljs-attr">Write</span>  <span class="hljs-attr">Error:</span> %<span class="hljs-attr">v</span> ", <span class="hljs-attr">err</span>)
                <span class="hljs-attr">client.Close</span>()
                <span class="hljs-attr">delete</span>(<span class="hljs-attr">s.clients</span>, <span class="hljs-attr">client</span>)
            }

        }

    }
}

<span class="hljs-attr">func</span> <span class="hljs-attr">getMessageTemplate</span>(<span class="hljs-attr">msg</span> *<span class="hljs-attr">Message</span>) []<span class="hljs-attr">byte</span> {
    <span class="hljs-attr">tmpl</span>, <span class="hljs-attr">err</span> <span class="hljs-attr">:</span>= <span class="hljs-string">template.ParseFiles(</span>"<span class="hljs-attr">views</span>/<span class="hljs-attr">message.html</span>")
    <span class="hljs-attr">if</span> <span class="hljs-attr">err</span> != <span class="hljs-string">nil</span> {
        <span class="hljs-attr">log.Fatalf</span>("<span class="hljs-attr">template</span> <span class="hljs-attr">parsing:</span> %<span class="hljs-attr">s</span>", <span class="hljs-attr">err</span>)
    }

    // <span class="hljs-attr">Render</span> <span class="hljs-attr">the</span> <span class="hljs-attr">template</span> <span class="hljs-attr">with</span> <span class="hljs-attr">the</span> <span class="hljs-attr">message</span> <span class="hljs-attr">as</span> <span class="hljs-attr">data.</span>
    <span class="hljs-attr">var</span> <span class="hljs-attr">renderedMessage</span> <span class="hljs-attr">bytes.Buffer</span>
    <span class="hljs-attr">err</span> = <span class="hljs-string">tmpl.Execute(&amp;renderedMessage,</span> <span class="hljs-attr">msg</span>)
    <span class="hljs-attr">if</span> <span class="hljs-attr">err</span> != <span class="hljs-string">nil</span> {
        <span class="hljs-attr">log.Fatalf</span>("<span class="hljs-attr">template</span> <span class="hljs-attr">execution:</span> %<span class="hljs-attr">s</span>", <span class="hljs-attr">err</span>)
    }

    <span class="hljs-attr">return</span> <span class="hljs-attr">renderedMessage.Bytes</span>()
}</span></span>
</code></pre><p>The <code>HandleWebSocket</code> function adds the client, processes the messages into JSON and then adds the message into a channel for distribution to all clients by <code>HandleMessage</code>. </p>
<p>It also keeps the connection alive. <code>getMessageTemplate</code> basically process the message into the <strong>message.html</strong>, and then converts it to a byte. This byte can then be sent to the client as a response. </p>
<h3 id="heading-how-to-add-a-websocket-to-routes-and-htmx">How to Add a WebSocket to Routes and HTMX</h3>
<p>We need to add the WebSocket to our routes <strong>main.go</strong>:</p>
<pre><code>package main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"github.com/gofiber/fiber/v2"</span>
    <span class="hljs-string">"github.com/gofiber/template/html/v2"</span>
    <span class="hljs-string">"github.com/gofiber/websocket/v2"</span>
    <span class="hljs-string">"github.com/steelthedev/go-chat/handlers"</span>
)

func main() {

    <span class="hljs-comment">// Start new fiber instance</span>
    <span class="hljs-attr">app</span> := fiber.New()

    <span class="hljs-comment">// Create a "ping" handler to test the server</span>
    app.Get(<span class="hljs-string">"/ping"</span>, func(ctx *fiber.Ctx) error{
        <span class="hljs-keyword">return</span> ctx.SendString(<span class="hljs-string">"Welcome to fiber"</span>)
    })

    <span class="hljs-comment">// create new App Handler</span>
    <span class="hljs-attr">appHandler</span> := NewAppHandler()

    <span class="hljs-comment">// Add appHandler routes</span>
    app.Get(<span class="hljs-string">"/, appHandler.HandleGetIndex)

    // create new webscoket
    server := NewWebSocket()
    app.Get("</span>/ws<span class="hljs-string">", websocket.New(func(ctx *websocket.Conn) {
        server.HandleWebSocket(ctx)
    }))

    go server.HandleMessages()


    // Start the http server
    app.Listen("</span>:<span class="hljs-number">3000</span><span class="hljs-string">")
}</span>
</code></pre><p>The WebSocket and its route has been added. The final step is to add the HTMX tags on the <strong>index.html</strong> file:</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;

&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;Chat Room&lt;/title&gt;
    &lt;script src="https://unpkg.com/htmx.org@1.9.10"
        integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC"
        crossorigin="anonymous"&gt;&lt;/script&gt;
    &lt;!-- HTMX Websockets extension https://htmx.org/extensions/web-sockets/ --&gt;
    &lt;script src="https://unpkg.com/htmx.org/dist/ext/ws.js"&gt;&lt;/script&gt;
    &lt;link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"&gt;
    &lt;link rel="stylesheet" href="/static/style.css"&gt;
&lt;/head&gt;

&lt;body&gt;
    &lt;div class="container"&gt;
        &lt;div class="chat-window" hx-ext="ws" ws-connect="/ws"&gt;
            &lt;div class="messages" id="messages" hx-swap="beforeend" hx-swap-oob="beforeend"&gt;
                &lt;!-- Messages will be appended here --&gt;
            &lt;/div&gt;
            &lt;form id="form" ws-send hx "&gt;
                &lt;div class="input-area"&gt;
                    &lt;input type="text" name="text" min="1" id="messageInput" placeholder="Type a message..."&gt;
                    &lt;button type="submit"&gt;Send&lt;/button&gt;
                &lt;/div&gt;
            &lt;/form&gt;

        &lt;/div&gt;
    &lt;/div&gt;

&lt;/body&gt;

&lt;/html&gt;
</code></pre><p>The <code>hx-ext</code> tag and <code>ws-connect</code> tag point to the WebSocket URL <code>/ws</code>. The <code>hx-swap</code> tag was used to perform DOM manipulations which adds our messages into the <code>#messages</code> div.</p>
<p>After saving this, run <code>go run main.go</code>. You can open two different browser windows at <code>localhost:3000</code></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/Screenshot-from-2024-06-02-04-23-06.png" alt="Image" width="600" height="400" loading="lazy">
<em>two browser windows used for sending and receiving messages</em></p>
<p>If the WebSocket is running perfectly, you should be able to send and receive messages from the two browsers in real-time as displayed in the picture.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In this tutorial, we have showcased how to create a simple WebSocket server using Go, Fiber and HTMX. </p>
<p>You can go on and improve this project by adding extra features such as ClientID, authentication and user management. You can visit the project repo here: <a target="_blank" href="https://github.com/steelthedev/go-chat">github.com/steelthedev/go-chat</a></p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
