Close Menu
    Trending
    • How to measure agent performance: metrics, methods, and ROI
    • MIT scientists debut a generative AI model that could create molecules addressing hard-to-treat diseases | MIT News
    • Why CrewAI’s Manager-Worker Architecture Fails — and How to Fix It
    • How to Implement Three Use Cases for the New Calendar-Based Time Intelligence
    • Ten Lessons of Building LLM Applications for Engineers
    • How to Create Professional Articles with LaTeX in Cursor
    • LLM Benchmarking, Reimagined: Put Human Judgment Back In
    • How artificial intelligence can help achieve a clean energy future | MIT News
    ProfitlyAI
    • Home
    • Latest News
    • AI Technology
    • Latest AI Innovations
    • AI Tools & Technologies
    • Artificial Intelligence
    ProfitlyAI
    Home » Javascript Fatigue: HTMX is all you need to build ChatGPT — Part 1
    Artificial Intelligence

    Javascript Fatigue: HTMX is all you need to build ChatGPT — Part 1

    ProfitlyAIBy ProfitlyAINovember 17, 2025No Comments13 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Reddit Telegram Email
    Share
    Facebook Twitter LinkedIn Pinterest Email


    was a time, way back, when constructing web sites was straightforward. HTML and CSS. It felt easy. These days, Javascript frameworks are all over the place. Relentless change, growing complexity. This phenomenon is named “Javascript Fatigue” and is all about builders exhausted by chasing the newest frameworks, construct instruments, libraries, and attempting to maintain the tempo. With HTMX, builders now have a approach to construct partaking net functions with higher simplicity and fewer burnout — and with out all of the JS trouble.

    A fascinating net software like ChatGPT, in lower than 200 strains of code, pure Python and HTML. Like this one:

    A fast refresher on how the Internet labored

    When Tim Berners-Lee created the primary net web page in 1990, the system he designed was largely a “read-only” system, that will result in pages related between themselves with hyperlinks, which everyone knows as anchor tags in HTML. HTML 1.0 was subsequently counting on one single tag and provided easy navigation between pages.

    <!-- The unique net: easy hypermedia -->
    <a href="/about">About Us</a>

    The anchor tag is a hypermedia management that does the next course of:

    • present the consumer that it is a hyperlink (clickable)
    • concern a GET request to the hyperlink URL

    When the server responds with a brand new web page, the browser will substitute the present web page with the brand new web page (navigation)

    Then got here Internet 2.0 which launched a brand new tag, the shape tag. This tag allowed to replace ressources along with studying them by way of the <a> tag. With the ability to replace ressources meant that we may actually begin constructing net functions. All of this with solely two controls: <type> and <a>.

    <!-- Internet 2.0: now we will replace information -->
    <type methodology="POST" motion="/login">
        <enter kind="e mail" identify="e mail" required>
        <enter kind="password" identify="password" required>
        <button kind="submit">Login</button>
    </type>

    The method when submitting a type is sort of just like the anchor tag, besides that we will:

    • select which form of request we wish to carry out (GET or POST)
    • connect consumer data like e mail, password, and many others. to be handed with the request

    The 2 tags are the one parts, in pure HTML, that may work together with a server.

    After which got here Javascript.

    JavaScript was initially created so as to add easy interactions to net pages: type validation, information fetching, and primary animations. However with the introduction of XMLHttpRequest (later often called AJAX), JavaScript advanced into one thing far more highly effective and complicated.

    With Javascript, builders may now set off HTTP requests with out the 2 tags, utilizing one thing referred to as AJAX. AJAX permits to fetch information from the server, and although XHR can fetch any kind of knowledge, together with uncooked HTML fragments, textual content, or XML, JSON grew to become the de facto information trade format.

    This implies there must be a further step the place JSON will get transformed to HTML, by way of a perform that renders HTML from JSON. As proven within the instance under, we proceed by:

    • fetching JSON information from the /api/customers endpoints (the response => response.json() half)
    • inserting this information right into a HTML templates (the const html half)
    • that may then be added to the DOM (the doc.getElementById() half)
    // The JavaScript manner: JSON → HTML conversion
    fetch('/api/customers')
        .then(response => response.json())
        .then(customers => {
            const html = customers.map(consumer => 
                `<div class="consumer">${consumer.identify}</div>`
            ).be a part of('');
            doc.getElementById('customers').innerHTML = html;
        });

    This rendering entails a decent coupling between the JSON information format and the perform itself: if the JSON information format modifications, it may break the HTML rendering perform. You already see one potential drawback right here, and this level is often a friction level between frontend and backend builders: frontend dev builds a UI based mostly on an anticipated JSON format, backend dev decides to alter the format, frontend dev must replace UI, backend dev modifications once more, frontend dev modifications once more, and many others.

    For some cause, net builders began placing JSON all over the place and managed all the things with JS. This result in what we name Single-Web page Purposes (SPAs): not like conventional HTML 2.0, we don’t navigate between pages anymore. All of the content material stays on one web page, and the content material is up to date with JS and UI rendering. That is how frameworks like React, Angular, Vue.js work.

    “The rising norm for net growth is to construct a React single-page software, with. server rendering. The 2 key parts of this structure are one thing like:
    – The principle UI is constructed & up to date in JavaScript utilizing React or one thing comparable.
    – The backend is an API that that software makes requests in opposition to.
    This concept has actually swept the web. It began with just a few main widespread web sites and has crept into corners like advertising websites and blogs.”

    (Tom MacWright, https://macwright.com/2020/05/10/spa-fatigue)

    Most present SPA architectures are “client-thick” functions the place many of the job happens on the client-side and the place the backend is merely an API returning JSON. This setup is thought for offering snappy and clean consumer experiences, however do we actually want that complexity each time?

    “(…) there are additionally quite a lot of issues for which I can’t see any concrete profit to utilizing React. These are issues like blogs, shopping-cart-websites, mostly-CRUD-and-forms-websites.”

    (Tom MacWright, https://macwright.com/2020/05/10/spa-fatigue)

    Javascript Fatigue is actual

    The “Javascript fatigue” is getting louder. It refers back to the principal drawbacks of SPA growth:

    • Rising complexity: Libraries and frameworks have grow to be more and more heavy and complicated, requiring massive groups to handle. Some opinionated frameworks additionally imply that JS builders need to specialize on one tech. No Python developer each referred to as itself “A Tensorflow Python developer”. They’re simply Python builders, and switching from TF to Pytorch nonetheless means you possibly can learn and use the 2.
    • Tight coupling: The coupling between information APIs and the UI creates friction inside groups. Breaking modifications happen on a regular basis, and there may be not approach to remedy this so long as groups use JSON as their trade interface.
    • Framework proliferation: The variety of frameworks retains growing, resulting in an actual feeling of “fatigue” amongst JS builders.
    • Over-engineering: You don’t want JS-heavy frameworks 90% of the time. And in some instances (content-heavy apps), it’s even a nasty thought.

    Aside from extremely interactive/collaborative UIs, easy HTML with Multi-Web page Purposes is usually sufficient.

    So what’s HTMX?

    HTMX is a really light-weight JS library (14k) that provides a HTML-centric method to constructing dynamic net functions. It extends HTML by permitting any ingredient to make AJAX requests and replace any a part of the DOM. Not like JS frameworks which do all of the rendering on the shopper facet, the heavy lifting is completed by the server by returning HTML fragments to be inserted within the DOM. This additionally implies that when you already know templating engines and HTML, the educational curve might be a lot a lot a lot simpler in comparison with studying React or Angular.

    As an alternative of abandoning hypermedia for JSON APIs, HTMX makes HTML extra succesful with the next:

    • Any ingredient could make HTTP requests (not simply <a> and <type>)
    • Any HTTP methodology (GET, POST, PUT, DELETE, PATCH)
    • Any ingredient may be focused for updates
    • Any occasion can set off requests (click on, submit, load, and many others.)

    In reality, you possibly can truly write your individual little GPT-like UI with HTMX and just some strains of Python!

    An actual demo: a ChatGPT app with HTMX and FastAPI

    For this text, we are going to construct somewhat chat with lower than 100 strains of Python and HTML. We are going to begin with quite simple demos to indicate how HTMX works, then add a easy chat UI, then add a streaming functionality to our chat. To make issues much more enticing, we are going to use the Google Agent Growth Toolkit, so we will leverage brokers in our chat!

    Easy HTMX demos

    Let’s assume we have now an API that returns a listing of customers. We wish to click on a button to fetch the info and show a listing.

    The standard, JS-way:

    <!-- Conventional JavaScript method -->
    <!DOCTYPE html>
    <html>
    
    <head>
      <title>Demo</title>
    </head>
    
    <physique>
      <h1>Customers</h1>
      <button onclick="getUsers()">Present</button>
      <div>
        <ul id="usersList">
        </ul>
      </div>
    
      <script>
        perform getUsers() {
          fetch('https://dummyjson.com/customers')
            .then(res => res.json())
            .then(information => {
              const usersList = doc.getElementById('usersList');
              if (usersList) {
                information.customers.forEach(consumer => {
                  const listItem = doc.createElement('li');
                  listItem.textContent = `${consumer.firstName} ${consumer.lastName}`;
                  usersList.appendChild(listItem);
                });
              }
            })
            .catch(error => {
              console.error('Error fetching customers:', error);
            });
        }
    
      </script>
    </physique>
    
    </html>

    And that is how you’d do with HTMX.

    First create your backend:

    from fastapi import FastAPI, Request
    from fastapi.responses import HTMLResponse
    from fastapi.templating import Jinja2Templates
    import requests
    
    app = FastAPI()
    templates = Jinja2Templates(listing="templates")
    
    @app.get("/", response_class=HTMLResponse)
    async def house(request: Request):
        return templates.TemplateResponse("demo.html", {"request": request})
    
    @app.get("/customers")
    async def get_users():
        r = requests.get("https://dummyjson.com/customers")
        information = r.json()
        html = ""
        for row in information['users']:
            html += f"<li>{row['firstName']} {row['lastName']}</li>n"
        return HTMLResponse(html)

    After which the HTML:

    <!-- Conventional JavaScript method -->
    <!DOCTYPE html>
    <html>
    
    <head>
      <title>Demo</title>
      <script src="https://cdn.jsdelivr.web/npm/[email protected]/dist/htmx.min.js" integrity="sha384-/TgkGk7p307TH7EXJDuUlgG3Ce1UVolAOFopFekQkkXihi5u/6OCvVKyz1W+idaz" crossorigin="nameless"></script>
    
    </head>
    
    <physique>
      <h1>Customers</h1>
       <button hx-get="/customers" hx-target="#usersList" hx-swap="innerHTML">Present</button>
      <div>
        <ul id="usersList">
        </ul>
      </div>
    </physique>

    And also you get precisely the identical outcome! What occurred simply right here? Have a look at the <button> ingredient. We see 3 attributes beginning with hx-. What are they right here for?

    • hx-get: Clicking on this button will set off a GET request to the /customers endpoint
    • hx-target: It tells the browser to interchange the content material of the ingredient which has the usersList id with the HTML information obtained from the server
    • hx-swap: It tells the browser to insert the HTML contained in the goal ingredient

    With that, you already know how you can use HTMX. The gorgeous factor about this manner of doing is that when you resolve altering your HTML, it received’t break something in your web page.

    There are, of programs, benefits and disadvantages in utilizing HTMX. However as a Python developer, it feels very good to mess around with my FastAPI backend and never fear loads about rendering HTML. Simply add Jinja templates, a dose of Tailwind CSS, and also you’re good to go!

    Our first chat with HTMX and FastAPI

    So now’s the second when issues are getting severe. What we are going to do, as a primary step, is construct a dumb chatbot that may take the customers question, and spit it backwards. For that we are going to construct a web page with:

    • a listing of messages
    • a textarea for the consumer’s enter

    And guess what, HTMX will deal with sending/receiving the messages! That is what the outcome will appear to be:

    Overview

    The stream is the next:

    1. Person inputs a question in a textarea
    2. This textarea is wrapped in a type, which is able to ship a POST request to the server with the question parameter.
    3. The backend receives the request, does one thing with the question (in actual life, we will use a LLM to reply the question). In our case, for demo functions, we are going to simply reply by reverting the question letter by letter.
    4. The backend wraps the response in an HTMLResponse (not JSON!)
    5. In our type, HTMX tells the browser the place to insert the response, as proven within the hx-target, and how you can swap it with the present DOM

    And that is all. So let’s start!

    Backend

    We are going to outline a /ship route that count on a question string from the frontend, inverts it, and sends it again in a <li> tag.

    from fastapi import FastAPI, Request, Kind
    from fastapi.templating import Jinja2Templates
    from fastapi.responses import HTMLResponse
    import asyncio
    import time
    
    app = FastAPI()
    templates = Jinja2Templates("templates")
    
    @app.get("/")
    async def root(request: Request):
        return templates.TemplateResponse(request, "simple_chat_sync.html")
    
    
    @app.publish("/ship")
    async def send_message(request: Request, question: str=Kind(...)):
        message = "".be a part of(checklist(question)[::-1])
        html = f"<li class='mb-6 justify-end flex'><div class='max-w-[70%] bg-black text-white rounded-xl px-4 py-2'><div class='font-bold text-right'>AI</div><div>{message}</div></div></li>"
        return HTMLResponse(html)

    Frontend

    On the frontend facet, we outline a easy HTML web page utilizing Tailwind CSS and HTMX:

    <!doctype html>
    <html>
    
    <head>
      <meta charset="UTF-8" />
      <meta identify="viewport" content material="width=device-width, initial-scale=1.0" />
      <script src="https://cdn.jsdelivr.web/npm/@tailwindcss/browser@4"></script>
      <hyperlink rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/spotlight.js/11.11.1/types/default.min.css">
      <script src="https://cdnjs.cloudflare.com/ajax/libs/spotlight.js/11.11.1/spotlight.min.js"></script>
      <script src="https://cdn.jsdelivr.web/npm/[email protected]/dist/htmx.min.js"
        integrity="sha384-/TgkGk7p307TH7EXJDuUlgG3Ce1UVolAOFopFekQkkXihi5u/6OCvVKyz1W+idaz"
        crossorigin="nameless"></script>
      <script src="https://cdn.jsdelivr.web/npm/[email protected]"
        integrity="sha384-A986SAtodyH8eg8x8irJnYUk7i9inVQqYigD6qZ9evobksGNIXfeFvDwLSHcp31N"
        crossorigin="nameless"></script>
      <hyperlink rel="preconnect" href="https://fonts.googleapis.com">
      <hyperlink rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
      <hyperlink href="https://fonts.googleapis.com/css2?household=Merriweather:[email protected]&show=swap" rel="stylesheet">
    
      <fashion>
        physique {
          font-family: "Merriweather";
        }
      </fashion>
    </head>
    
    <physique class="flex w-full bg-white h-screen">
      <principal class="flex flex-col w-full md:w-3/4 lg:w-1/2 pb-4 justify-between items-left mx-auto ">
        <header class="border-b p-4 text-2xl text-right">
          // ZeChat
        </header>
        <div class="mb-auto max-h-[80%] overflow-auto">
          <ul id="chat" class="rounded-2xl p-4 mb-16 justify-start">
    
          </ul>
        </div>
    
        <footer class="p-4 border-t">
          <type id="userInput" class="flex max-h-16 gap-4" hx-post="/ship" hx-swap="beforeend" hx-target="#chat"
            hx-trigger="click on from:#submitButton" hx-on::before-request="
                    htmx.discover('#chat').innerHTML += `<li class='mb-6 justify-start flex'><div class='max-w-[70%] border border-black rounded-xl px-4 py-2'><div class='font-bold'>Me</div><div>${htmx.discover('#question').worth}</div></div></li>`;
                    htmx.discover('#question').worth = '';
                    ">
            <textarea id="question" identify="question"
              class="flex w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 min-h-[44px] max-h-[200px]"
              placeholder="Write a message..." rows="4"></textarea>
            <button kind="submit" id="submitButton"
              class="inline-flex max-h-16 items-center justify-center rounded-md bg-neutral-950 px-6 font-medium text-neutral-50 transition energetic:scale-110">Ship</button>
    
          </type>
        </footer>
      </principal>
    </physique>
    
    </html>

    Let’s have a more in-depth look the <type> tag. This tag has a number of attributes, so let’s take a minute to evaluation them:

    • hx-post="/ship": It’s going to make a POST request to the /ship endpoint.
    • hx-trigger="click on from:#submitButton": This implies the request might be triggered when the submitButtonis clicked
    • hx-target="#chat": This tells the browser the place to place the HTML response. In that case, we wish the response to be appended to the checklist.
    • hx-swap="beforeend": The hx-target tells the place to place the content material, the hx-swap tells HOW. In that case, we wish the content material to be added earlier than the tip (so after the final baby)

    The hx-on::before-request is somewhat bit extra advanced, however may be defined simply. It mainly occurs between the press and the second the request is shipped. It’s going to add the consumer enter the underside of the checklist, and clear the consumer enter. This manner, we get a quick consumer expertise!

    A Higher chat (streaming + LLM)

    What we constructed is a quite simple but purposeful chat, nonetheless if we wish to plug a LLM, we would have some instances when the response from the server takes a very long time. The way in which our present chat is constructed is synchronous, that means nothing will occur till the LLM is completed writing. Not an excellent consumer expertise.

    What we’d like now’s streaming, and an actual LLM to have a dialog with. And that is Half 2.



    Source link

    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email
    Previous ArticleSandbars AI-ring Stream fångar dina tankar
    Next Article The Absolute Beginner’s Guide to Pandas DataFrames
    ProfitlyAI
    • Website

    Related Posts

    Artificial Intelligence

    MIT scientists debut a generative AI model that could create molecules addressing hard-to-treat diseases | MIT News

    November 25, 2025
    Artificial Intelligence

    Why CrewAI’s Manager-Worker Architecture Fails — and How to Fix It

    November 25, 2025
    Artificial Intelligence

    How to Implement Three Use Cases for the New Calendar-Based Time Intelligence

    November 25, 2025
    Add A Comment
    Leave A Reply Cancel Reply

    Top Posts

    From Reactive to Predictive: Forecasting Network Congestion with Machine Learning and INT

    July 18, 2025

    Modular Arithmetic in Data Science

    August 19, 2025

    Fears of AI’s Impact Create New Political Alliances and Tensions

    November 19, 2025

    Cyberbrottslingar använder Vercels v0 för att skapa falska inloggningssidor

    July 3, 2025

    Need a research hypothesis? Ask AI. | MIT News

    April 7, 2025
    Categories
    • AI Technology
    • AI Tools & Technologies
    • Artificial Intelligence
    • Latest AI Innovations
    • Latest News
    Most Popular

    Regeringens AI-satsning: Myndigheter ska kunna dela känslig data

    June 18, 2025

    LLM Monitoring and Observability: Hands-on with Langfuse

    August 25, 2025

    Shaip Announces Successful Completion of SOC 2 Type 2 Audit for Shaip Data Platform

    April 7, 2025
    Our Picks

    How to measure agent performance: metrics, methods, and ROI

    November 25, 2025

    MIT scientists debut a generative AI model that could create molecules addressing hard-to-treat diseases | MIT News

    November 25, 2025

    Why CrewAI’s Manager-Worker Architecture Fails — and How to Fix It

    November 25, 2025
    Categories
    • AI Technology
    • AI Tools & Technologies
    • Artificial Intelligence
    • Latest AI Innovations
    • Latest News
    • Privacy Policy
    • Disclaimer
    • Terms and Conditions
    • About us
    • Contact us
    Copyright © 2025 ProfitlyAI All Rights Reserved.

    Type above and press Enter to search. Press Esc to cancel.