<?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: Turcu Laurentiu</title>
    <description>The latest articles on DEV Community by Turcu Laurentiu (@turculaurentiu91).</description>
    <link>https://dev.to/turculaurentiu91</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1176100%2Fc6bde9b0-566b-482a-916f-7ea7ce94670a.jpeg</url>
      <title>DEV Community: Turcu Laurentiu</title>
      <link>https://dev.to/turculaurentiu91</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/turculaurentiu91"/>
    <language>en</language>
    <item>
      <title>Why you should choose HTMX for your next project</title>
      <dc:creator>Turcu Laurentiu</dc:creator>
      <pubDate>Thu, 19 Oct 2023 05:21:36 +0000</pubDate>
      <link>https://dev.to/turculaurentiu91/why-you-should-choose-htmx-for-your-next-project-o7j</link>
      <guid>https://dev.to/turculaurentiu91/why-you-should-choose-htmx-for-your-next-project-o7j</guid>
      <description>&lt;p&gt;In this article, we will aim to understand why you should consider HTMX as a replacement for React next time you choose a tech stack for a web app. We will look at the complexity and the challenges that a traditional HTTP JSON API + React brings and how easily you can avoid them by using HTMX.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: In this article I will address to React but it can be replaced with any other Front-end framework like Angular or Vue or Svelte or Solid, but I talk about React because it's the default technology that most of the web developers default to.&lt;/p&gt;

&lt;h3&gt;
  
  
  What even is HTMX
&lt;/h3&gt;

&lt;p&gt;If you don't know already, &lt;a href="https://htmx.org/" rel="noopener noreferrer"&gt;HTMX&lt;/a&gt; is a small browser (JS) library that extends HTML with a few attributes that allows you to update parts of a web page with some response from the server. It also enables HTML to make HTTP requests on all the verbs not only GET and POST.&lt;/p&gt;

&lt;h2&gt;
  
  
  What problem does React solve
&lt;/h2&gt;

&lt;p&gt;React is a JavaScript library that helps you write highly interactive applications by keeping the user interface in sync with the state. You tell it how to render a given state, and every time the state is updated, it will re-render ( as efficiently as they can ) the UI to reflect the state changes.&lt;/p&gt;

&lt;p&gt;Every time the state has changed, you notify the library that it changed and you provide the new state and it will deal with the UI updates.&lt;/p&gt;

&lt;p&gt;Examples of high interactive apps that needs local in memory state can be one of the various text editors that you can find on the web (VSCode), a drag and drop kanban board as Trello or JIRA, a video player or a chat room.&lt;/p&gt;

&lt;p&gt;What is not an example of such an app? The to-do list you are building, the news website you are reading, the blog you are posting on and most of the websites around. If we were to look at the &lt;a href="https://en.wikipedia.org/wiki/Pareto_principle" rel="noopener noreferrer"&gt;80/20 rule&lt;/a&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;80% of effects come from 20% of causes and 80% of results come from 20% of effort.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can argue that 80% of the web apps that uses React don't have the need of a local state. And from those 20% that needs it, you can argue that it's only a small portion of the app (about 20%) and the rest can be expressed only in HTML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The numbers are made up, I don't have any research to back this up&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What react also solves that made it widely adopted for modern websites
&lt;/h2&gt;

&lt;p&gt;HTML is old and outdated. The old ways of making applications with HTML involved a collection of pages, links and forms that describes to the user the current state of a given resource and what they can do to change it.&lt;/p&gt;

&lt;p&gt;Every time the user interacted with a resource, the application could only reload the whole page to display the new state of the resource.&lt;/p&gt;

&lt;p&gt;A couple of years later FaceBook introduced React, a JS library that allowed developers to create single page applications (SPA). No more full page reloads when navigating and cool transitions for state updates, interesting feedback to the user and other niceties that made the web developers adopt SPA Frameworks for their websites.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Complexity Issue
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxpk0v0nz0d45hv91i166.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxpk0v0nz0d45hv91i166.png" alt="AI Generated image to showcase the crazy complexity of modern apps with next.js"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you don't understand the above schema, don't worry, there is nothing to understand. I asked ChantGPT to generate it for me, and since it's over-complicated and it doesn't make any sense, it perfectly reflects the current default infrastructure for a modern web app.&lt;/p&gt;

&lt;p&gt;One cool programming principle is &lt;strong&gt;KISS&lt;/strong&gt; which stands for Keep it Stupid Simple, or how some might like to joke, Keep it Simple, Stupid! &lt;/p&gt;

&lt;p&gt;Current infrastructure and tech stack that modern developers defaults to create web apps is extremely complicated, doing a lot of things that it doesn't have to, just because it's cool!&lt;/p&gt;

&lt;p&gt;And works fine, when you are building the first POC by yourself, but the next moment you add more team members, and an agile way of working with multiple iteration and "embracing" the changes, it kind of breaks, for the reasons we will take a look down the line.&lt;/p&gt;

&lt;h2&gt;
  
  
  The State Management Problem with traditional HTTP JSON API + React
&lt;/h2&gt;

&lt;p&gt;What you often have to do in a web app is to get the state of a resource from the database and present it to the user. Let's take the example of a task management application. The user has a list of tasks, each task has as a state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The title of the task&lt;/li&gt;
&lt;li&gt;A description&lt;/li&gt;
&lt;li&gt;A flag if the task was completed&lt;/li&gt;
&lt;li&gt;A due date (optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We usually store this state in a database and to present this information to the user you have to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get all the tasks from the database where the user has access to.&lt;/li&gt;
&lt;li&gt;Optionally transform the data (maybe you store the date it was completed and you compute the &lt;code&gt;is_completed&lt;/code&gt; flag from that).&lt;/li&gt;
&lt;li&gt;Serialize the data into JSON.&lt;/li&gt;
&lt;li&gt;Fetch the data via an HTTP request.&lt;/li&gt;
&lt;li&gt;(optionally but usually) validate the data against a schema, probably with YUP or ZOD.&lt;/li&gt;
&lt;li&gt;transform the JSON into state and store it in a cache using Redux, Zustand, react-query or another state management library.&lt;/li&gt;
&lt;li&gt;Transform that state in HTML usually figuring out what the user can do with the data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a nutshell, we are describing how to render all the possible states of all the resources in JavaScript, download the said JavaScript in the browser, then the JavaScript downloads a bunch  of data in JSON format and render it (if it knows how) on the browser as HTML!&lt;/p&gt;

&lt;p&gt;This is a lot of work to show a list of tasks to the user, especially when the tasks only changes when the user changes it and to do so, the app has to put the app in a loading state, make another HTTP request (to PUT or PATCH or DELETE) invalidate the cached value (the state) and re-fetch it to display the changed task.&lt;/p&gt;

&lt;p&gt;Or even worse, when the user changes some task, optimistically update the local state and show the change right away and perform the request to update behind the scenes only to notify the user that the update failed after they sow it successfully updating.&lt;/p&gt;

&lt;p&gt;This is extremely error prone. It might work well for this to-do app where you are the only developer and the app is mall enough that you can keep a mental map of everything that is happening. But when you have a larger team, especially when you split the team between front-end and back-end, a lot of issues can arise from miscommunication. &lt;/p&gt;

&lt;p&gt;The back-end might use the &lt;code&gt;is_completed&lt;/code&gt; flag while the front-end might expect an &lt;code&gt;is_active&lt;/code&gt; flag. The back-end might send the &lt;code&gt;description&lt;/code&gt; processed from markdown to HTML while the front-end might expect it to be unprocessed. The back-end might make the &lt;code&gt;description&lt;/code&gt; optional to allow the users to save drafts while the front-end is not in sync and you see a lot of &lt;code&gt;Uncaught TypeError: Cannot read properties of undefined (reading 'toLowerCase')&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;On the other hand, on HTMX, you render the HTML directly on the template, as typesafe as your backend language allows it. You send only the relevant information to the browser, you present the user with the appropriate controls on the resource and you instruct the browser or HTMX how to interpret the user actions and the backend response to those actions. All the application state is the HTML, a  concept known as &lt;a href="https://htmx.org/essays/hateoas/" rel="noopener noreferrer"&gt;HATEOAS&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The need for documentation for traditional HTTP JSON API + React
&lt;/h2&gt;

&lt;p&gt;In order to have two teams (back-end and front-end) work independently and communicate via HTTP JSON API, you need to have proper documentation of the API. You also need to document how to calculate what actions a user can take on a given resource in order to display the controls.&lt;/p&gt;

&lt;p&gt;Most of this kind of documentation is a pain to write, especially because usually it's required to write before it's implemented, when the developer doesn't yet fully understand the scope of the problem so the front-end can be developed in parallel. This usually ends up with many updates while in development to adjust for the problems that arises during the development and can lead to miss-aligned versions between the teams.&lt;/p&gt;

&lt;p&gt;You also need to version the API and be careful to not introduce breaking changes on a non major version changes. You can no longer change the name of a field without bumping the major version. You also need to ether keep multiple versions of the API running or force the front-end team to adapt.&lt;/p&gt;

&lt;p&gt;And most of the time, the documentation get's outdated. Some must be urgently fixed, some new requirements come the day before release and now your documentation is out of date, even for a short period of time. And you have to remember to update it, or even worse, you create a ticket to remember it, and somebody else picks it up that doesn't have the whole picture and documents it wrong!&lt;/p&gt;

&lt;h2&gt;
  
  
  The duplicated logic issue
&lt;/h2&gt;

&lt;p&gt;For each resource, you have to implement authorization policies. You must determine if the current user can mark the task &lt;strong&gt;46234&lt;/strong&gt; as completed. Somewhere in the back-end code you must write this check. Otherwise you leave your app open to &lt;em&gt;insecure direct object reference&lt;/em&gt;, or anyone with Postman can mark your task done.&lt;/p&gt;

&lt;p&gt;You also have to implement the same logic in the front-end, to show the mark button only if the user has the rights to mark is as complete (let's pretend that you can share your tasks with other users, but only you can change them).&lt;/p&gt;

&lt;p&gt;Now every time this logic changes, you must implement it in both applications, and release it at the same time or have multiple versions of the API.&lt;/p&gt;

&lt;h2&gt;
  
  
  The performance issue
&lt;/h2&gt;

&lt;p&gt;In order to have a website rendered in the browser with React, you need to bundle together the react code that has a gigantic footprint on memory and parsing/processing impact, the state management library code, toe UI library code, the CSS-IN-JS library code, the application code and whatever js library we install and use with NPM (and we are not shy to install a new package, see the &lt;a href="https://www.theregister.com/2016/03/23/npm_left_pad_chaos/" rel="noopener noreferrer"&gt;leftpad problem&lt;/a&gt;). This results in usually chunky JavaScript assets to be delivered via network. Sure, you can cache in the browser, but on modern agile development, you deploy at least once per sprint, so that solves nothing. This consumes network traffic and battery, a much ignored problem for mobile devices.&lt;/p&gt;

&lt;p&gt;The above mentioned JavaScript needs to be interpreted by the browser, thus consuming processing power and battery.&lt;/p&gt;

&lt;p&gt;The JavaScript, especially the ReactDOM, needs to keep track of a mirror of the DOM. Add the normal DOM on top of that and the local state cache, and all the render functions, and all the &lt;code&gt;useMemo&lt;/code&gt; and &lt;code&gt;useCallback&lt;/code&gt; and &lt;code&gt;useState&lt;/code&gt;.  Also add all the closures that needs to keep in memory all the context variables. And JavaScript engines are not known for their memory efficiency! You hear people lamenting about how much memory the browser consumes, but they underestimate how much that comes form the websites they visit.&lt;/p&gt;

&lt;p&gt;All those adds up and you end up draining users batteries and memory. Sure, you can put the effort and optimize all that, or use another library like Svelte, but all that effort can be put into delivering more meaningful features for your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  The need for server side rendering
&lt;/h2&gt;

&lt;p&gt;In recent years, we sow the rise of the server side rendering specialized frameworks like &lt;code&gt;Next.js&lt;/code&gt;. Their popularity highlights the needs for content to be delivered in HTML format, especially for accessibility optimization, performance and search engine optimization reasons. &lt;/p&gt;

&lt;p&gt;You don't want to wait for the browser to download the JavaScript to render the page, then wait for the JavaScript to make HTTP requests to get the contents and then render it, you want it to be rendered right away, especially for the above the fold content.&lt;/p&gt;

&lt;p&gt;This adds another layer of complexity, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The infrastructure, now you need another server for the front-end app too&lt;/li&gt;
&lt;li&gt;The code is more complex, including the mental map of what code runs on the server and what on the browser&lt;/li&gt;
&lt;li&gt;The deployment pipelines are now more complex&lt;/li&gt;
&lt;li&gt;The testing infrastructure is now more complex&lt;/li&gt;
&lt;li&gt;Troubleshooting an issue is now harder, you need to understand if the issue is on the browser, on the client app server or on the API server&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solving those problems
&lt;/h2&gt;

&lt;p&gt;The web development community, each on their own language or technologies they develop on, tied to solve those problems in different ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js (and Nuxt and alike)

&lt;ul&gt;
&lt;li&gt;React server components&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Laravel

&lt;ul&gt;
&lt;li&gt;Inertia.JS&lt;/li&gt;
&lt;li&gt;Livewire&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;DotNet

&lt;ul&gt;
&lt;li&gt;Blazor Pages&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Elixir

&lt;ul&gt;
&lt;li&gt;Phonix LiveView&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Rust

&lt;ul&gt;
&lt;li&gt;Leptos Server Functions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;And many other solutions that I forgot about or never heard about!&lt;br&gt;
Anyway, the existence and popularity of such solutions is the proof that those issues are valid and encountered in the daily life of a web developer. Otherwise they wouldn't go out of their way to solve them, especially in an open source manner!&lt;/p&gt;

&lt;p&gt;There is also &lt;a href="https://turbo.hotwired.dev/" rel="noopener noreferrer"&gt;Turbo&lt;/a&gt; and the frameworks who adopt them, Ruby on Rails, PHP Symphony and possibly others that solves the same issue in the same manner as HTMX. And the choice for HTMX is only a personal taste in this, but you should definitely learn about this, this is as cool as HTMX!&lt;/p&gt;

&lt;p&gt;Among all those, HTMX stands out, not only because it doesn't lock you in to a specific technology, you can switch from PHP to Rust with minor changes to the templates, but to the fact that it completely removes the need for stateful components, or the need to keep track of a certain state of the app that is not resource related.&lt;/p&gt;

&lt;p&gt;For example, let's take a confirmation dialog modal. What you usually end up doing, is that you have a local in memory state if it's open, and display it to the user based on that state. In HTMX, the state &lt;strong&gt;IS THE HTML&lt;/strong&gt;  meaning that when you click on open modal, you &lt;strong&gt;GET&lt;/strong&gt; the &lt;code&gt;tasks/{taskId}/confirm-delete&lt;/code&gt; and embed the response HTML in the DOM. And when it's deleted, you delete the modal and the task altogether! This solves all the above mentioned problems in an unique and extremely simple manner, you don't need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep track of the state&lt;/li&gt;
&lt;li&gt;know how to render the dialog&lt;/li&gt;
&lt;li&gt;document the API&lt;/li&gt;
&lt;li&gt;check if the user can delete the task ( in the front-end)&lt;/li&gt;
&lt;li&gt;your back-end app is always in charge&lt;/li&gt;
&lt;li&gt;you get better security as you don't send irrelevant data to the browser and sneak sensitive info&lt;/li&gt;
&lt;li&gt;you get better performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;And most importantly, you keep your app dead simple, and allow complexity only when it solves user problems!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You just instruct HTMX where to get the dialog from, and where to put it, and it's all handled!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;


&lt;span class="c"&gt;&amp;lt;!-- the delete button --&amp;gt;&lt;/span&gt;
@if ($chirp-&amp;gt;user-&amp;gt;is(auth()-&amp;gt;user()))
    &lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;  
        @csrf  
        @method('delete')  
        &lt;span class="nt"&gt;&amp;lt;x-dropdown-link&lt;/span&gt;  
            &lt;span class="na"&gt;:component=&lt;/span&gt;&lt;span class="s"&gt;"'button'"&lt;/span&gt;  
            &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;  
            &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.confirm-destroy', $chirp) }}"&lt;/span&gt;  
            &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"beforeend"&lt;/span&gt;  
            &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"closest .chirp"&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;    
            {{ __('Delete') }}  
        &lt;span class="nt"&gt;&amp;lt;/x-dropdown-link&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
@endif


&lt;span class="c"&gt;&amp;lt;!-- the dialog template --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"modal fixed z-10 inset-0 overflow-y-auto flex justify-center items-center bg-black bg-opacity-50"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"backdrop-filter: blur(14px);"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-white rounded p-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-xl border-b pb-2 mb-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Confirm Action&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Are you sure you want to delete this chirp?&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex justify-end mt-4 gap-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;x-secondary-button&lt;/span&gt; &lt;span class="na"&gt;_=&lt;/span&gt;&lt;span class="s"&gt;"on click remove closest .modal"&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                Cancel
            &lt;span class="nt"&gt;&amp;lt;/x-secondary-button&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;                
                @csrf  
                &lt;span class="nt"&gt;&amp;lt;x-danger-button&lt;/span&gt;  
                    &lt;span class="na"&gt;hx-delete=&lt;/span&gt;&lt;span class="s"&gt;"{{route('chirps.destroy', $chirp)}}"&lt;/span&gt;  
                    &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"closest .chirp"&lt;/span&gt;  
                    &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"delete"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
                        Delete  
                &lt;span class="nt"&gt;&amp;lt;/x-danger-button&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;    
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;this example is from my tutorial on [HTMX with Laravel](&lt;a href="https://dev.to/turculaurentiu91/laravel-htmx--g0n"&gt;https://dev.to/turculaurentiu91/laravel-htmx--g0n&lt;/a&gt;, check it out!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And just like that, we instruct HTMX, when we click on the delete button, to do a &lt;strong&gt;GET&lt;/strong&gt; request to the &lt;code&gt;chirps/{chirp}/confirm-destroy&lt;/code&gt; and put the resulted HTML before the closest parent &lt;code&gt;&amp;lt;div class="chirp"&amp;gt;&lt;/code&gt; ends (at the bottom). And in the delete dialog, when the user confirms, we instruct HTMX to do a &lt;strong&gt;DELETE&lt;/strong&gt; request, to the &lt;code&gt;chirps/{chirp}&lt;/code&gt; endpoint, and when successful, we delete the closest parent with the &lt;code&gt;chirp&lt;/code&gt; class.&lt;/p&gt;

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

&lt;p&gt;In the ever-evolving landscape of web development, it's refreshing to see tools like HTMX that advocate for simplicity and a return to the basics. By leveraging the power of HTML and HTTP, HTMX allows developers to create dynamic web applications without the complexities and overhead of traditional JavaScript frameworks.&lt;/p&gt;

&lt;p&gt;So, next time you're starting a new project or considering refactoring an existing one, give HTMX a try. You might be surprised at how much you can achieve with so little.&lt;/p&gt;

</description>
      <category>htmx</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Laravel + HTMX = ❤️ 3x</title>
      <dc:creator>Turcu Laurentiu</dc:creator>
      <pubDate>Sat, 14 Oct 2023 09:51:52 +0000</pubDate>
      <link>https://dev.to/turculaurentiu91/laravel-htmx-3x-ge0</link>
      <guid>https://dev.to/turculaurentiu91/laravel-htmx-3x-ge0</guid>
      <description>&lt;p&gt;Welcome to the final installment of our Chirper tutorial series. In this part, we will refine our platform by introducing a few advanced features: an active search mechanism, more robust form validation, and a user-friendly confirmation dialog for chirp deletion. These enhancements are aimed at not only improving the user experience but also showcasing the flexibility and power of combining Laravel with HTMX and Hyperscript. Let's delve in and give Chirper a polished edge!&lt;/p&gt;

&lt;h2&gt;
  
  
  Searching trough chirps
&lt;/h2&gt;

&lt;p&gt;On a more complex app you might want a different search engine/database and you might want to integrate with &lt;a href="https://laravel.com/docs/10.x/scout"&gt;Laravel Scout&lt;/a&gt; and Angolia. However, for the purpose of this tutorial, a simple &lt;code&gt;LIKE&lt;/code&gt; query would have been fine, if I didn't generated a huge amount of chirps on my test server.&lt;/p&gt;

&lt;p&gt;In order to gain some performance and keep my test server simple and cheap, I will go with &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/fulltext-search.html"&gt;MySQL FULLTEXT indexes&lt;/a&gt;and raw queries. It's a good enough search mechanism that you can follow if you have &lt;code&gt;MySQL&lt;/code&gt;. If you are using anything else, replace the scope with a &lt;code&gt;LIKE&lt;/code&gt; and it will work just fine.&lt;/p&gt;

&lt;h3&gt;
  
  
  The full text index
&lt;/h3&gt;

&lt;p&gt;MySQL offers a full text index to be applied on &lt;code&gt;VARCHAR&lt;/code&gt;s and &lt;code&gt;TEXT&lt;/code&gt; fields that you can later use to perform natural language search in your tables, let's create a migration to apply the index.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan make:migration add_full_text_index_on_chirps_message &lt;span class="nt"&gt;--table&lt;/span&gt; chirps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;  

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Database\Migrations\Migration&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Database\Schema\Blueprint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Schema&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Migration&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="cd"&gt;/**  
     * Run the migrations.     
     **/&lt;/span&gt;    
     &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;  
    &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Blueprint&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
            &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="no"&gt;Illuminate\Support\Facades\DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ALTER TABLE chirps ADD FULLTEXT fulltext_index (message)'&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="cd"&gt;/**  
     * Reverse the migrations.     
     **/&lt;/span&gt;    
     &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;down&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;  
    &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Blueprint&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
            &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="no"&gt;Illuminate\Support\Facades\DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;statement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ALTER TABLE chirps DROP INDEX fulltext_index'&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;p&gt;Cool, now apply it with &lt;code&gt;php artisan migrate&lt;/code&gt;, this might take a couple of seconds, depending on how many chirps you have.&lt;/p&gt;

&lt;h3&gt;
  
  
  Legacy form submit search
&lt;/h3&gt;

&lt;p&gt;Let's start by adding a normal HTML form that will perform the search action, and later we will enhance it with HTMX to search while you type. In the &lt;code&gt;chirps.index&lt;/code&gt; template, we will add the form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/chirps/index.blade.php --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;x-app-layout&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"max-w-2xl mx-auto p-4 sm:p-6 lg:p-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  

        &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt;            
            &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;  
            &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.index') }}"&lt;/span&gt;  
            &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center space-x-2 mb-4 w-full bg-white border-1 border-gray-300 px-4 py-2 rounded-md shadow-sm focus-within:ring focus-within:border-indigo-200 focus-within:ring-indigo-200 focus-within:ring-opacity-50"&lt;/span&gt;  

        &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;                
            &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;  
                &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt;  
                &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt;  
                &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-full bg-white border-0 p-0 focus:ring-0 focus:border-0 focus:outline-none"&lt;/span&gt;  
                &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Search for chirps"&lt;/span&gt;  
                &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ request('search') }}"&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;                
                &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;  
                &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-gray-100 p-1 rounded hover:bg-gray-400 transition"&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
                &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt; &lt;span class="na"&gt;stroke-width=&lt;/span&gt;&lt;span class="s"&gt;"1.5"&lt;/span&gt; &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-6 h-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
                    &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;stroke-linecap=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt; &lt;span class="na"&gt;stroke-linejoin=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;  
                &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;            
            &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;        
        &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- rest of the template --&amp;gt;&lt;/span&gt;

    &lt;span class="c"&gt;&amp;lt;!-- long pooling div --&amp;gt;&lt;/span&gt;
    @if($chirps-&amp;gt;count() &amp;gt; 0)  
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"{{  
                route('chirps.pool', [  
                    'latest_from' =&amp;gt; $chirps-&amp;gt;first()-&amp;gt;created_at-&amp;gt;toISOString(),  
                    'search' =&amp;gt; request('search')]) }}"&lt;/span&gt;  
             &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"every 2s"&lt;/span&gt;  
             &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;  
    @endif

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a simple HTML form, that will make a &lt;strong&gt;GET&lt;/strong&gt; request to the same index page with the &lt;code&gt;?search&lt;/code&gt; query param. We also modified the long pooling &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, we firstly wrapped it in an &lt;code&gt;@if&lt;/code&gt; to render it only if there are chirps (to avoid an error when there are no chirps) and we also added the &lt;code&gt;search&lt;/code&gt; query param to the route function, to avoid loading chirps that don't satisfy the search query.&lt;/p&gt;

&lt;p&gt;Now let's add a search scope in the chirps model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/Models/Chirp.php&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;scopeSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Builder&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;mixed&lt;/span&gt; &lt;span class="nv"&gt;$search&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Builder&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="nb"&gt;is_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$search&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="nv"&gt;$search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$search&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;explode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&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="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'+'&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$word&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&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="nb"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;whereRaw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'MATCH(message) AGAINST(? IN BOOLEAN MODE)'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$search&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;orderByRaw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'MATCH(message) AGAINST(? IN BOOLEAN MODE) DESC'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$search&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="nv"&gt;$query&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;What this scope is doing, if the &lt;code&gt;$search&lt;/code&gt; param is a string, it will explode it into words, prepend a &lt;code&gt;+&lt;/code&gt; and append an &lt;code&gt;*&lt;/code&gt; to each word. The &lt;code&gt;+&lt;/code&gt; means that the message must include the word and the &lt;code&gt;*&lt;/code&gt; means that the matched word must start with.&lt;/p&gt;

&lt;p&gt;The order by will order by relevance.&lt;/p&gt;

&lt;p&gt;And we can use the scope in the &lt;code&gt;index&lt;/code&gt; and &lt;code&gt;pool&lt;/code&gt; controller methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/Http/Controllers/ChirpController.php&lt;/span&gt;

&lt;span class="cd"&gt;/**  
 * Display a listing of the resource. 
 **/&lt;/span&gt;
 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  

    &lt;span class="nv"&gt;$chirps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Chirp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user'&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;latest&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;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'search'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'search'&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;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;response&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;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps.index'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
        &lt;span class="s1"&gt;'chirps'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$chirps&lt;/span&gt;  
    &lt;span class="p"&gt;]);&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;  

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="nv"&gt;$validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;  
        &lt;span class="s1"&gt;'latest_from'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|date'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="p"&gt;]);&lt;/span&gt;  

    &lt;span class="nv"&gt;$chirps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Chirp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user'&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'created_at'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$validated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'latest_from'&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'!='&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&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;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'search'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'search'&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;latest&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;get&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="nv"&gt;$chirps&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&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="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;response&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;noContent&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="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;response&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;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps.pool'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
        &lt;span class="s1"&gt;'chirps'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$chirps&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;We only added the when statements, to use the search query scope only when the search query param is present on the request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Active search
&lt;/h3&gt;

&lt;p&gt;This is cool and all and it works just fine, but there is no HTMX magic here excluding the boosting. So let's add some HTMX properties on the search input to search when the user finished typing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/chirps/index.blade.php --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;  
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;  
    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt;  
    &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt;  
    &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-full bg-white border-0 p-0 focus:ring-0 focus:border-0 focus:outline-none"&lt;/span&gt;  
    &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Search for chirps"&lt;/span&gt;  
    &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ request('search') }}"&lt;/span&gt;  
    &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"keyup changed delay:500ms"&lt;/span&gt;  
    &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.index') }}"&lt;/span&gt;  
    &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#chirps"&lt;/span&gt;  
    &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;  
    &lt;span class="na"&gt;hx-select=&lt;/span&gt;&lt;span class="s"&gt;"#chirps"&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all we need to add for the active search. What we instruct HTMX is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;hx-trigger&lt;/code&gt;: when the keyup event fires on this input, if the value changed we delay for 500ms and if no new event happens we do the request&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hx-get&lt;/code&gt;: perform a get request to the index, it will add the search query param using the name of the field to decide how to name it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hx-target&lt;/code&gt; &lt;code&gt;hx-swap&lt;/code&gt; and &lt;code&gt;hx-select&lt;/code&gt;: it will select the &lt;code&gt;&amp;lt;div id="#chirps"&lt;/code&gt; from the response and it will replace the whole div for the target with the same ID.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Form Validation
&lt;/h2&gt;

&lt;p&gt;So far, across our app, we just ignored form validation, in this section we will take a look at how to display back-end validation messages using HTMX.&lt;/p&gt;

&lt;h3&gt;
  
  
  Edit Chirp Form
&lt;/h3&gt;

&lt;p&gt;When editing the chirp in the &lt;code&gt;ChirpController::update&lt;/code&gt;, if the validation fails, the response throws an &lt;code&gt;ValidationError&lt;/code&gt; and Laravel's default behavior in a multi page app is to redirect  back with &lt;code&gt;302&lt;/code&gt; and make the validation &lt;code&gt;$errors&lt;/code&gt; available for the template.&lt;/p&gt;

&lt;p&gt;We like that for the cases when JS is disabled, however, when making requests with HTMX we want to modify this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/Http/Controllers/ChirpController.php&lt;/span&gt;

&lt;span class="cd"&gt;/**
     * Update the specified resource in storage.
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Chirp&lt;/span&gt; &lt;span class="nv"&gt;$chirp&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;RedirectResponse&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nc"&gt;Response&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'update'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$chirp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|string|max:255'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;]);&lt;/span&gt;

            &lt;span class="nv"&gt;$chirp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$validated&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="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HX-Request'&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="nf"&gt;response&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;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'components.chirps.single'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="s1"&gt;'chirp'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$chirp&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="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps.index'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ValidationException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&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="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HX-Request'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nv"&gt;$e&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="nf"&gt;response&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;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'components.chirps.edit'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'chirp'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$chirp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'errors'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;errors&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;What we did here is we wrapped everything in a try/catch statement and caught the &lt;code&gt;ValidationException&lt;/code&gt;. Then, if this is not an HTMX request, we throw it back and leave Laravel to handle it (no JS case).&lt;/p&gt;

&lt;p&gt;If it's an HTMX request, we return the edit form, and we make the &lt;code&gt;$e-&amp;gt;errors()&lt;/code&gt; available to the template in a collection. The HTMX behavior if this form is to replace itself with the HTML from the response.&lt;/p&gt;

&lt;p&gt;Give it a try!&lt;/p&gt;

&lt;h3&gt;
  
  
  The Create Chirp Form
&lt;/h3&gt;

&lt;p&gt;The create chirp form is a bit more complicated because we target the &lt;code&gt;#chirps&lt;/code&gt; div with the response instead of itself. For this, we will take advantage of the &lt;a href="https://htmx.org/docs/#oob_swaps"&gt;out of band swaps&lt;/a&gt; (OOB) in HTMX.&lt;/p&gt;

&lt;p&gt;This means that those elements marked with &lt;code&gt;hx-swap-oob="true"&lt;/code&gt; won't follow the normal &lt;code&gt;hx-swap&lt;/code&gt; but it will match the elements by ID and swap them. There is one rule, the OOB elements in the response must be at the root level, but this works just fine for our case.&lt;/p&gt;

&lt;p&gt;Let's start by extracting the create chirp form into a component and add and ID and the OOB property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/components/chirps/create.blade.php --&amp;gt;&lt;/span&gt;

@props(['oob' =&amp;gt; true])

&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt;
    &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"chirps-create"&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="err"&gt;($&lt;/span&gt;&lt;span class="na"&gt;oob&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
        &lt;span class="na"&gt;hx-swap-oob=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;endif&lt;/span&gt;
    &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;
    &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.store') }}"&lt;/span&gt;
    &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.store') }}"&lt;/span&gt;
    &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#chirps"&lt;/span&gt;
    &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"afterbegin"&lt;/span&gt;
    &lt;span class="na"&gt;hx-on=&lt;/span&gt;&lt;span class="s"&gt;"htmx:afterRequest: if(event.detail.successful) this.reset();"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    @csrf
    &lt;span class="nt"&gt;&amp;lt;textarea&lt;/span&gt;
        &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;
        &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"{{ __('What\'s on your mind?') }}"&lt;/span&gt;
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block w-full border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ old('message') }}&lt;span class="nt"&gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;x-input-error&lt;/span&gt; &lt;span class="na"&gt;:messages=&lt;/span&gt;&lt;span class="s"&gt;"$errors-&amp;gt;get('message')"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-2"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;x-primary-button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ __('Chirp') }}&lt;span class="nt"&gt;&amp;lt;/x-primary-button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the old form in the index template with this new component&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/chirps/index.blade.php --&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Just below the search form, replace the old create form with the new component --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;x-chirps.create&lt;/span&gt; &lt;span class="na"&gt;:oob=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/x-chirps.create&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's do the same with the &lt;code&gt;store&lt;/code&gt; method on the chirps controller, let's wrap it with a try/catch and if we have HTMX request and we have errors, we render the form component only.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;
&lt;span class="cd"&gt;/**  
 * Store a newly created resource in storage. 
 **/&lt;/span&gt;
 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;RedirectResponse&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nc"&gt;Response&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="nv"&gt;$validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;  
            &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|string|max:255'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
        &lt;span class="p"&gt;]);&lt;/span&gt;  

        &lt;span class="nv"&gt;$chirp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;user&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;chirps&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;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$validated&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="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HX-Request'&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="nf"&gt;response&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;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'components.chirps.single'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
                &lt;span class="s1"&gt;'chirp'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$chirp&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="nf"&gt;redirect&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;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps.index'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ValidationException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&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="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HX-Request'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nv"&gt;$e&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="nf"&gt;response&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;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'components.chirps.create'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
            &lt;span class="s1"&gt;'errors'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;errors&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;p&gt;And that's all, now if you try to create a new chirp without a message, you will see the validation error. &lt;/p&gt;

&lt;p&gt;However, there is a problem. If you successfully create a new chirp while having a validation error, the validation error persists, let's fix that!&lt;/p&gt;

&lt;p&gt;We can add the form in the single chirp template, but we don't need to add it always, but only when we want.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/components/chirps/single.blade.php --&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- At the top of the template --&amp;gt;&lt;/span&gt;

@props(['chirp', 'withCreateForm' =&amp;gt; false])

@if($withCreateForm)
    &lt;span class="nt"&gt;&amp;lt;x-chirps.create&amp;gt;&amp;lt;/x-chirps.create&amp;gt;&lt;/span&gt;
@endif
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;$withCreateForm&lt;/code&gt; prop, we can control if we want to also render the create form, usually we don't so we set &lt;code&gt;false&lt;/code&gt; as a default.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;store&lt;/code&gt; method on the chirp controller, if we are successful, we return the single chirp template with the create form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/Http/Controllers/ChirpController.php&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HX-Request'&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="nf"&gt;response&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;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'components.chirps.single'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'chirp'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$chirp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'withCreateForm'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&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;And on the create form, we can drop the &lt;code&gt;hx-on&lt;/code&gt; script, because now we reset it by replacing the whole &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Confirmation Dialog
&lt;/h2&gt;

&lt;p&gt;On our current implementation, when you want to delete a chirp, you just delete it. To make it safer for the users, we can add a confirmation dialog. HTMX comes with a &lt;code&gt;hx-confirm&lt;/code&gt; attribute that takes advantage of the browser confirmation dialogs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/components/chirps/single.blade.php --&amp;gt;&lt;/span&gt;

 &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;
      &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.destroy', $chirp) }}"&lt;/span&gt;
      &lt;span class="na"&gt;hx-delete=&lt;/span&gt;&lt;span class="s"&gt;"{{route('chirps.destroy', $chirp)}}"&lt;/span&gt;
      &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"closest .chirp"&lt;/span&gt;
      &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"delete"&lt;/span&gt;
      &lt;span class="na"&gt;hx-confirm=&lt;/span&gt;&lt;span class="s"&gt;"Are you sure you want to delete this chirp?"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when you delete a chirp the browser asks you if you are sure about this before deleting. However, this is not the best user experience possible, we want to render HTML modals.&lt;/p&gt;

&lt;p&gt;To do so, we will need to first create a new component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/components/chirps/confirm-destroy.blade.php --&amp;gt;&lt;/span&gt;

@props(['chirp'])  

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"modal fixed z-10 inset-0 overflow-y-auto flex justify-center items-center bg-black bg-opacity-50"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"backdrop-filter: blur(14px);"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-white rounded p-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-xl border-b pb-2 mb-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Confirm Action&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Are you sure you want to delete this chirp?&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex justify-end mt-4 gap-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;x-secondary-button&lt;/span&gt; &lt;span class="na"&gt;_=&lt;/span&gt;&lt;span class="s"&gt;"on click remove closest .modal"&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                Cancel
            &lt;span class="nt"&gt;&amp;lt;/x-secondary-button&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;                
                @csrf  
                &lt;span class="nt"&gt;&amp;lt;x-danger-button&lt;/span&gt;  
                    &lt;span class="na"&gt;hx-delete=&lt;/span&gt;&lt;span class="s"&gt;"{{route('chirps.destroy', $chirp)}}"&lt;/span&gt;  
                    &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"closest .chirp"&lt;/span&gt;  
                    &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"delete"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
                        Delete  
                &lt;span class="nt"&gt;&amp;lt;/x-danger-button&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;    
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will render a dialog modal with a title and some content and two buttons.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The cancel button will just delete the closest .dialog from the DOM using Hyperscript&lt;/li&gt;
&lt;li&gt;The second will make a delete request, and it will delete the closest .chirp from the DOM on success.
Note that we had to wrap it in a &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; to include the &lt;code&gt;@csrf&lt;/code&gt; blade attribute to render the CSRF hidden input.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we need a new method on the chirps controller to render this dialog&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/Http/Controllers/ChirpController.php&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;confirmDestroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Chirp&lt;/span&gt; &lt;span class="nv"&gt;$chirp&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&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;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'components.chirps.confirm-destroy'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'chirp'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$chirp&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;And we need to add it on the routes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// routes/web.php&lt;/span&gt;

&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps/pool'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;ChirpController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'pool'&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;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps.pool'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps/{chirp}/confirm-destroy'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;ChirpController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'confirmDestroy'&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;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps.confirm-destroy'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ChirpController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;only&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'index'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'show'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'store'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'edit'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'update'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'destroy'&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;middleware&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'auth'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'verified'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we need to modify the single chirp template, that when we click on the delete button, it will get the dialog and insert it in the DOM rather than deleting the chirp.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;
&lt;span class="c"&gt;&amp;lt;!-- resources/views/components/chirps/single.blade.php --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;  
      &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.destroy', $chirp) }}"&lt;/span&gt;  
      &lt;span class="na"&gt;hx-delete=&lt;/span&gt;&lt;span class="s"&gt;"{{route('chirps.destroy', $chirp)}}"&lt;/span&gt;  
      &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"closest .chirp"&lt;/span&gt;  
      &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"delete"&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    @csrf  
    @method('delete')  
    &lt;span class="nt"&gt;&amp;lt;x-dropdown-link&lt;/span&gt;  
        &lt;span class="na"&gt;:component=&lt;/span&gt;&lt;span class="s"&gt;"'button'"&lt;/span&gt;  
        &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;  
        &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.confirm-destroy', $chirp) }}"&lt;/span&gt;  
        &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"beforeend"&lt;/span&gt;  
        &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"closest .chirp"&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
        {{ __('Delete') }}  
    &lt;span class="nt"&gt;&amp;lt;/x-dropdown-link&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also deleted the &lt;code&gt;hx-confirm&lt;/code&gt; attribute on the form. We kept the old delete form here to remain usable when the JS is disabled.&lt;/p&gt;

&lt;p&gt;The only missing piece is some animations to make the user experience better, so let's define two custom animations in tailwind:&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="c1"&gt;// tailwind.config.js&lt;/span&gt;

&lt;span class="nx"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="nl"&gt;fontFamily&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="na"&gt;sans&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;Figtree&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="nx"&gt;defaultTheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fontFamily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sans&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  
    &lt;span class="p"&gt;},&lt;/span&gt;  
    &lt;span class="nx"&gt;animation&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;fade-in&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;fade-in 0.2s ease-out&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;fade-out&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;fade-out 0.2s ease-out&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="nx"&gt;keyframes&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;fade-in&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0%&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="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&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;100%&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="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fade-out&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0%&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="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&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;100%&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="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&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="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And modify the confirm-destroy template to make use of Hyperscript to animate the modal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;
&lt;span class="c"&gt;&amp;lt;!-- resources/views/components/chirps/confirm-destroy.blade.php --&amp;gt;&lt;/span&gt;

@props(['chirp'])

@props(['chirp'])  

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;  
    &lt;span class="na"&gt;_=&lt;/span&gt;&lt;span class="s"&gt;"on closeModal(destroy)  
            add .animate-fade-out            
            then wait for animationend            
            then if destroy remove closest .chirp else remove me        
        end        
            on click if (event.target == event.currentTarget) trigger closeModal"&lt;/span&gt;    
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"modal fixed z-10 inset-0 overflow-y-auto flex justify-center items-center bg-black bg-opacity-50 animate-fade-in"&lt;/span&gt;  
        &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"backdrop-filter: blur(14px);"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-white rounded p-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-xl border-b pb-2 mb-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Confirm Action&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Are you sure you want to delete this chirp?&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex justify-end mt-4 gap-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;x-secondary-button&lt;/span&gt; &lt;span class="na"&gt;_=&lt;/span&gt;&lt;span class="s"&gt;"on click trigger closeModal"&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Cancel&lt;span class="nt"&gt;&amp;lt;/x-secondary-button&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;                @csrf  
                &lt;span class="nt"&gt;&amp;lt;x-danger-button&lt;/span&gt;  
                    &lt;span class="na"&gt;hx-delete=&lt;/span&gt;&lt;span class="s"&gt;"{{route('chirps.destroy', $chirp)}}"&lt;/span&gt;  
                    &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"closest .chirp"&lt;/span&gt;  
                    &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt;  
                    &lt;span class="na"&gt;_=&lt;/span&gt;&lt;span class="s"&gt;"on htmx:afterRequest trigger closeModal(destroy:true)"&lt;/span&gt;  
                    &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
                        Delete  
                &lt;span class="nt"&gt;&amp;lt;/x-danger-button&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;        
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;    
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First of all, on the parent div, we added the &lt;code&gt;.animate-fade-in&lt;/code&gt;  class that will animate when inserted into dom.&lt;/p&gt;

&lt;p&gt;Clicking on the buttons, when we click on cancel button a custom &lt;code&gt;closeModal&lt;/code&gt; event is dispatched. Also, we changed the &lt;code&gt;hx-swap&lt;/code&gt; to none when deleting, and translated the &lt;code&gt;htmx:afterRequest&lt;/code&gt; into a &lt;code&gt;closeModal&lt;/code&gt; with destroy set to true.&lt;/p&gt;

&lt;p&gt;We also added quite some chunk of Hyperscript on the parent div:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;on closeModal(destroy)  
    add .animate-fade-out            
    then wait for animationend            
    then if destroy remove closest .chirp else remove me        
end        
    on click if (event.target == event.currentTarget) trigger closeModal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This instructs Hyperscript to listen for &lt;code&gt;closeModal&lt;/code&gt; events and:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the &lt;code&gt;animate-fade-out&lt;/code&gt; class to animate it out&lt;/li&gt;
&lt;li&gt;wait for the &lt;code&gt;animationend&lt;/code&gt; event, or in English, wait for animation to end&lt;/li&gt;
&lt;li&gt;if destroy is truthful, then we delete the parent chirp, otherwise we delete only the dialog&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second part of the script instructs Hyperscript to trigger a &lt;code&gt;closeModal&lt;/code&gt; on itself if the &lt;code&gt;target&lt;/code&gt; is the &lt;code&gt;currentTarget&lt;/code&gt;, thus closing the modal when clicking on the background.&lt;/p&gt;

&lt;p&gt;Thus adding some nice fade in and out animations for the dialog.&lt;/p&gt;

&lt;h2&gt;
  
  
  The conclusion
&lt;/h2&gt;

&lt;p&gt;Over the course of these tutorials, we've journeyed through the process of creating a dynamic, real-time application using a blend of Laravel, HTMX, and Hyperscript, culminating in a functional Chirper platform. These technologies have facilitated a modern, interactive user experience without the overhead typically associated with heavy front-end frameworks.&lt;/p&gt;

&lt;p&gt;In part one, we laid the groundwork by setting up the Laravel application, defining the necessary routes, controllers, and views. The Chirper platform began to take shape as we created a simple interface for users to post chirps.&lt;/p&gt;

&lt;p&gt;Part two saw the introduction of real-time functionality through HTMX, allowing users to see new chirps as they are posted by others, without needing to refresh the page. This was a key step in enhancing the user interaction and making the platform more engaging.&lt;/p&gt;

&lt;p&gt;In the final part, we delved deeper into enhancing user experience by implementing a full-text search functionality, refining form validation, and introducing a user-friendly confirmation dialog for chirp deletion. These enhancements were aimed at making the platform more robust, user-friendly, and ready for real-world use.&lt;/p&gt;

&lt;p&gt;The live version of the Chirper project can be explored at &lt;a href="https://chirper.tlaurentiu.net/"&gt;https://chirper.tlaurentiu.net&lt;/a&gt;. For those interested in the code behind the platform, the entire project is available on GitHub: &lt;a href="https://github.com/turculaurentiu91/chirper-htmx"&gt;https://github.com/turculaurentiu91/chirper-htmx&lt;/a&gt;. This project serves as a practical example of how modern web technologies can be leveraged to build interactive, real-time applications with a minimal front-end footprint.&lt;/p&gt;

&lt;p&gt;This tutorial series demonstrates not only the technical steps required to build such a platform but also the thought process and decision-making involved in choosing technologies and implementing features. It's a glimpse into the possibilities that open up when blending server-side frameworks like Laravel with client-side libraries like HTMX and Hyperscript.&lt;/p&gt;

&lt;p&gt;We hope this series has been informative and inspiring, encouraging you to explore these technologies and perhaps integrate them into your future projects.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>htmx</category>
      <category>laravel</category>
      <category>php</category>
    </item>
    <item>
      <title>Laravel + HTMX = ❤️ 2x</title>
      <dc:creator>Turcu Laurentiu</dc:creator>
      <pubDate>Sat, 07 Oct 2023 08:41:33 +0000</pubDate>
      <link>https://dev.to/turculaurentiu91/laravel-htmx-2x-4o48</link>
      <guid>https://dev.to/turculaurentiu91/laravel-htmx-2x-4o48</guid>
      <description>&lt;p&gt;In this exciting second installment of our tutorial series, we will venture further into the realm of modern web application development with Laravel and HTMX. Our main focus will be on enriching the user experience on the index page by introducing real-time updates for new chirps. Initially, we'll employ a polling strategy to fetch fresh chirps at regular intervals. As we progress, we'll transition from polling to a more dynamic and efficient approach using Web-Sockets, ensuring that new chirps are displayed to users as they are posted, in real time.&lt;/p&gt;

&lt;p&gt;If you didn't already, checkout the &lt;a href="https://dev.to/turculaurentiu91/laravel-htmx--g0n"&gt;first part&lt;/a&gt; of this tutorial. You can also checkout the full implementation on &lt;a href="https://github.com/turculaurentiu91/chirper-htmx"&gt;this GitHub repository&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Real-Time Updates: The Polling Edition
&lt;/h2&gt;

&lt;p&gt;So far, our simple HTMX implementation has been working smoothly, and users are chirping away happily. However, the product owner has pointed out a less than ideal user experience: to view new chirps, users need to manually refresh the page. In response to this feedback, we'll be implementing a polling mechanism using HTMX to fetch new chirps from the server periodically.&lt;/p&gt;

&lt;p&gt;Let's begin by creating a new controller action to handle the polling request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// app/Http/Controllers/ChirpController.php&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="nv"&gt;$validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;  
        &lt;span class="s1"&gt;'latest_from'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|date'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="p"&gt;]);&lt;/span&gt;  

    &lt;span class="nv"&gt;$chirps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Chirp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user'&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'created_at'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$validated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'latest_from'&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;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'!='&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&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;latest&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;get&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="nv"&gt;$chirps&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&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="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;response&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;noContent&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="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;response&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;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps.pool'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
        &lt;span class="s1"&gt;'chirps'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$chirps&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;In this action:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We validate the input to ensure &lt;code&gt;latest_from&lt;/code&gt; is a properly formatted date.&lt;/li&gt;
&lt;li&gt;We query the database for chirps that are newer than the &lt;code&gt;latest_from&lt;/code&gt; date, excluding chirps from the current user.&lt;/li&gt;
&lt;li&gt;If no new chirps are found, we return a &lt;code&gt;204 No Content&lt;/code&gt; response. Otherwise, we render the &lt;code&gt;chirps.pool&lt;/code&gt; template.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, let's adjust the &lt;code&gt;chirps.index&lt;/code&gt; template to include a new div responsible for polling new chirps from the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;
&lt;span class="c"&gt;&amp;lt;!-- resources/views/chirps/index.blade.php --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;x-data=&lt;/span&gt;&lt;span class="s"&gt;"{noscriptFix: true}"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"chirps"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-6 bg-white shadow-sm rounded-lg divide-y"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.pool', ['latest_from' =&amp;gt; $chirps-&amp;gt;first()-&amp;gt;created_at-&amp;gt;toISOString()]) }}"&lt;/span&gt;  
         &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"every 2s"&lt;/span&gt;  
         &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

    @foreach ($chirps as $chirp)  
        &lt;span class="nt"&gt;&amp;lt;x-chirps.single&lt;/span&gt; &lt;span class="na"&gt;:chirp=&lt;/span&gt;&lt;span class="s"&gt;"$chirp"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;  
    @endforeach  

    @if($chirps-&amp;gt;nextPageUrl())  
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;  
            &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"{{ $chirps-&amp;gt;nextPageUrl() }}"&lt;/span&gt;  
            &lt;span class="na"&gt;hx-select=&lt;/span&gt;&lt;span class="s"&gt;"#chirps&amp;gt;div.chirp,#chirps&amp;gt;div.chirps-paginator"&lt;/span&gt;  
            &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;  
            &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"intersect"&lt;/span&gt;  
            &lt;span class="na"&gt;x-cloak&lt;/span&gt;  
            &lt;span class="na"&gt;x-if=&lt;/span&gt;&lt;span class="s"&gt;"noscriptFix"&lt;/span&gt;  
            &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chirps-paginator"&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
            Loading more...  
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;  
    @endif
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also updated the way paginator work, to select only the chirps and the &lt;code&gt;.chirps-paginator&lt;/code&gt; div so we don't include the pooling element twice. We only need one pooling div, at the top of the list.&lt;/p&gt;

&lt;p&gt;Additionally, we'll need to create a new template, &lt;code&gt;chirps.pool&lt;/code&gt;, which will return the new chirps along with a self-replacing div to continue the polling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;
&lt;span class="c"&gt;&amp;lt;!-- resources/views/chirps/pool.blade.php --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.pool', ['latest_from' =&amp;gt; $chirps-&amp;gt;first()-&amp;gt;created_at-&amp;gt;toISOString()]) }}"&lt;/span&gt;  
     &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"every 2s"&lt;/span&gt;  
     &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;  

@foreach($chirps as $chirp)  
    &lt;span class="nt"&gt;&amp;lt;x-chirps.single&lt;/span&gt; &lt;span class="na"&gt;:chirp=&lt;/span&gt;&lt;span class="s"&gt;"$chirp"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;  
@endforeach
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note the duplicated div that pools, you can abstract in a component if you wish to be DRY&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In essence, we instruct HTMX to send a GET request every two seconds to the &lt;code&gt;chirps.pool&lt;/code&gt; route. If new chirps are found, HTMX will replace the entire content of the polling div with the new chirps and a fresh polling div, ensuring that the polling continues with the updated &lt;code&gt;latest_from&lt;/code&gt; date.&lt;/p&gt;

&lt;p&gt;This mechanism efficiently delivers real-time updates to our users without requiring a page refresh, significantly enhancing the user experience on our platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-Time Updates: Introducing Web Sockets
&lt;/h2&gt;

&lt;p&gt;For simple updates like this pooling might be just enough, and you can optimize a bit further by increasing the pool time to five seconds or more. But let's pretend that some users lamented that with the new update, the website it's consuming their data plan and it's draining their battery, so our PO comes around and asks us for a more optimal solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Broadcasting events with Laravel
&lt;/h3&gt;

&lt;p&gt;Laravel has a nice an easy way to broadcast events via web-sockets. The only problem in using it with HTMX is that you communicate with JSON rather than with HTML, and thus we are stranding away from the concept of &lt;a href="https://htmx.org/essays/hateoas/"&gt;HATEOAS&lt;/a&gt; (Hypermedia as the Engine of Application State).&lt;/p&gt;

&lt;p&gt;We can make it work with Laravel events and HTMX web-sockets extension by rendering the HTML string in some field in the event JSON on the back-end, and on the front-end we can register an event listener for &lt;a href="https://htmx.org/extensions/web-sockets/#htmx:wsBeforeMessage"&gt;htmx:wsBeforeMessage&lt;/a&gt; and transform the event in such a way that &lt;code&gt;event.detail.message&lt;/code&gt; contains only the HTML string.&lt;/p&gt;

&lt;p&gt;But this is complicated, and if I like something, I like simple and straightforward approaches. So what we are going to do is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We are going to use Laravel ECHO to receive the web-socket broadcast events&lt;/li&gt;
&lt;li&gt;When we receive the events, we translate in a custom browser event &lt;code&gt;chirper:newChirp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;We fetch new chirps via HTMX when the event fires, preferably debounced&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I will use &lt;a href="https://docs.soketi.app/"&gt;soketi&lt;/a&gt; for this, but you can use whatever you want. I will not include instructions on how to setup Laravel, you can follow the &lt;a href="https://laravel.com/docs/10.x/broadcasting"&gt;official documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will reuse the &lt;code&gt;chirps/pool&lt;/code&gt; endpoint and template, we will have to modify the template a little bit&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;
&lt;span class="c"&gt;&amp;lt;!-- resources/views/chirps/pool.blade.php --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;  
    &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"chirper:chirpCreated throttle:500 from:body"&lt;/span&gt;  
    &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.pool', ['latest_from' =&amp;gt; $chirps-&amp;gt;first()-&amp;gt;created_at-&amp;gt;toISOString()]) }}"&lt;/span&gt;  
    &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;  

@foreach($chirps as $chirp)  
    &lt;span class="nt"&gt;&amp;lt;x-chirps.single&lt;/span&gt; &lt;span class="na"&gt;:chirp=&lt;/span&gt;&lt;span class="s"&gt;"$chirp"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;  
@endforeach
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We basically changed the trigger from &lt;code&gt;every 2s&lt;/code&gt; to &lt;code&gt;chirper:chirpCreated throttle:500 from:body&lt;/code&gt;, meaning that every type there is an chirp-created event triggered on body, throttle for 500ms and then get the data.&lt;/p&gt;

&lt;p&gt;Make sure you do the same change for &lt;code&gt;chirps/index.blade.php&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;resources/js/app.js&lt;/code&gt; ( make sure you have Echo configured according to your setup)&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="nx"&gt;laravelEcho&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chirps&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="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ChirpCreated&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="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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chirper:chirpCreated&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This bit of JS will convert the &lt;code&gt;ChirpCreated&lt;/code&gt; event from the &lt;code&gt;chirps&lt;/code&gt; channel to a &lt;code&gt;CustomEvent&lt;/code&gt; on the body of the document.&lt;/p&gt;

&lt;p&gt;And in the PHP part of the app, we need to make the &lt;code&gt;ChirpCreated&lt;/code&gt; event to implement &lt;code&gt;ShouldBroadcast&lt;/code&gt; and specify the channel it should broadcast, namely &lt;code&gt;chirps&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;  

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Events&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\Chirp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Broadcasting\Channel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Broadcasting\InteractsWithSockets&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Broadcasting\PresenceChannel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Broadcasting\PrivateChannel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Contracts\Broadcasting\ShouldBroadcast&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Foundation\Events\Dispatchable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Queue\SerializesModels&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChirpCreated&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ShouldBroadcast&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Dispatchable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;InteractsWithSockets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SerializesModels&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  

    &lt;span class="cd"&gt;/**  
     * Create a new event instance.     
     * */&lt;/span&gt;    
     &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;Chirp&lt;/span&gt; &lt;span class="nv"&gt;$chirp&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="cd"&gt;/**  
     * Get the channels the event should broadcast on.     
     *     
     * @return array&amp;lt;int, Channel&amp;gt;  
     */&lt;/span&gt;    
     &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;broadcastOn&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Channel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps'&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;p&gt;And that should be it. Now we only fetch the chirps when a new chirp is created. You can have a more advanced solution where you broadcast to private channels and broadcast &lt;code&gt;toOthers&lt;/code&gt; but it's just fine for the scope of this tutorial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. I will not deploy the web-sockets on my test server, it's a hustle to deploy soketi and to setup nginx and such, so it will just showcase the pooling version. No For the next sections, we will start from the end of the pooling section.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Scripting
&lt;/h2&gt;

&lt;p&gt;Now, with real-time updates, the page refreshes itself with new chirps from the server. However, a challenge arises when a user scrolls down to read older chirps—they won't be aware of new chirps inserted at the top of the page.&lt;/p&gt;

&lt;p&gt;A solution is to introduce a floating button whenever new chirps are inserted. This button, when clicked, will scroll the page to the top, making the user aware of the new chirps.&lt;/p&gt;

&lt;p&gt;Scripting is essential for implementing this feature. While HTMX provides the &lt;a href="https://htmx.org/attributes/hx-on/"&gt;hx-on attribute&lt;/a&gt; which could be sufficient for simpler tasks like resetting a form, our scenario requires an intersection observer to remove the button when the user scrolls to the top—either manually or by clicking the button.&lt;/p&gt;

&lt;p&gt;Here's where Hyperscript comes into play.  &lt;a href="https://hyperscript.org/"&gt;Hyperscript&lt;/a&gt; is designed to simplify event-based scripting in the browser, making it more human-readable. Although we won't delve deeply into its syntax, feel free to explore it on their website.&lt;/p&gt;

&lt;p&gt;Next, we'll walk through the steps to integrate Hyperscript and implement the floating button feature to enhance user awareness of new chirps.&lt;/p&gt;

&lt;p&gt;We need to install the script, so in the app layout, after the HTMX script, we add another script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/layouts/app.blade.php --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/htmx.org@1.9.6"&lt;/span&gt; &lt;span class="na"&gt;integrity=&lt;/span&gt;&lt;span class="s"&gt;"sha384-FhXw7b6AlE/jyjlZH5iHa/tTe9EpJ1Y55RjcgPbjeWMskSxZt1v9qkxLJWNJaGni"&lt;/span&gt; &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/hyperscript.org@0.9.11"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the chirps pool template we add the following HTML, above the new chirps foreach&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/chirps/pool.blade.php --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; 
     &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"border-transparent opacity-0"&lt;/span&gt;   
    &lt;span class="na"&gt;_=&lt;/span&gt;&lt;span class="s"&gt;"on intersection(intersecting) if intersecting remove me else remove .opacity-0"&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fixed top-4 left-0 right-0 flex justify-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt;            
            &lt;span class="na"&gt;_=&lt;/span&gt;&lt;span class="s"&gt;"on click window.scrollTo({ top: 0, behavior: 'smooth' })"&lt;/span&gt;  
            &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;  
            &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"py-2 px-4 bg-indigo-600 hover:bg-indigo-800 rounded-full text-white shadow-lg flex justify-center items-center gap-4"&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; 
                &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; 
                &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt; 
                &lt;span class="na"&gt;stroke-width=&lt;/span&gt;&lt;span class="s"&gt;"1.5"&lt;/span&gt; 
                &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt; 
                &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-6 h-6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
                &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; 
                    &lt;span class="na"&gt;stroke-linecap=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt; 
                    &lt;span class="na"&gt;stroke-linejoin=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt; 
                    &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M12 19.5v-15m0 0l-6.75 6.75M12 4.5l6.75 6.75"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;  
            Bew Chirps  
        &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This peace of HTML consists in two wrapper &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements, one to position the element in the list and another one to fix the button position at the top of the page.&lt;/p&gt;

&lt;p&gt;The parent &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; element has an interesting attribute, &lt;code&gt;_="on intersection(intersecting) if intersecting remove me else remove .opacity-0"&lt;/code&gt;. This will instruct Hyperscript to install an intersection observer for this div, that on intersection, if is intersecting, to remove the element otherwise remove the &lt;code&gt;opacity-0&lt;/code&gt; class. So when the user scrolls to the top ether naturally or by clicking on the button, it will disappear.&lt;/p&gt;

&lt;p&gt;The opacity is to avoid the button flickering on the screen when the user is already at the top of the page.&lt;/p&gt;

&lt;p&gt;On the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element, we also have an Hyperscript attribute &lt;code&gt;_="on click window.scrollTo({ top: 0, behavior: 'smooth' })"&lt;/code&gt; that will scroll the page to the top. It's the same as &lt;code&gt;onclick="window.scrollTo({ top: 0, behavior: 'smooth' })"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can do more advanced scripting with Hyperscript, you can insert classes, you can wait for animations, etc. etc. &lt;/p&gt;

&lt;p&gt;As a homework, I encourage you to look at the documentation, and insert a slide down animation when the button is inserted and add a slide-up animation before the button is removed.&lt;/p&gt;

&lt;p&gt;And that's all, now when the pooling gets new chirps, it will also insert the button and inform the user about the button presence.&lt;/p&gt;





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

&lt;p&gt;We've come a long way in transforming Chirper from a simple Blade project to a modern web app with real-time updates and a user-friendly notification system. Leveraging HTMX and Hyperscript, we were able to enhance our application with infinite scrolling and web sockets, all while keeping the implementation straightforward and readable. Our journey highlights the power and simplicity these libraries bring to the table, enabling us to build dynamic features with less complexity compared to heavier frameworks.&lt;/p&gt;

&lt;p&gt;Our exploration doesn't end here. We've merely scratched the surface of what's possible with HTMX, Hyperscript, and Laravel. As we move forward, we'll continue to refine our application, ensuring it not only meets our users' needs but also provides a delightful user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;In the upcoming third part of this series, we'll dive into implementing an active search functionality to help users easily find specific chirps. We'll also focus on enhancing the robustness of our application by utilizing Laravel's validation features to display form errors, ensuring a smooth and informative user interaction. Furthermore, we'll add a layer of confirmation dialogs for deleting chirps, providing a safer user experience and minimizing accidental data loss.&lt;/p&gt;

&lt;p&gt;Through these enhancements, we aim to make Chirper a more interactive, user-friendly, and robust platform. So, stay tuned for more insights and practical implementations in the next installment of our journey with Chirper!&lt;/p&gt;

&lt;p&gt;In the meantime, feel free to explore and experiment with the code we've built so far. There's a lot to learn and a myriad of ways to further refine and expand upon what we've created. Until next time, happy coding!&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>html</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Laravel + HTMX = ❤️</title>
      <dc:creator>Turcu Laurentiu</dc:creator>
      <pubDate>Tue, 03 Oct 2023 16:10:53 +0000</pubDate>
      <link>https://dev.to/turculaurentiu91/laravel-htmx--g0n</link>
      <guid>https://dev.to/turculaurentiu91/laravel-htmx--g0n</guid>
      <description>&lt;h2&gt;
  
  
  Enhancing Chirper: A Journey from Blade to a Modern Web App
&lt;/h2&gt;

&lt;p&gt;In this article, we are going to explore how we can utilize HTMX alongside Laravel to modernize the &lt;a href="https://bootcamp.laravel.com/blade/installation"&gt;Laravel Bootcamp&lt;/a&gt; Chirper blade project. As a primer, feel free to explore the live implementation at &lt;a href="http://chirper.tlaurentiu.net/"&gt;chirper.tlaurentiu.net&lt;/a&gt; to get a hands-on feel of what we'll be delving into.&lt;br&gt;
If you don't wish to register, you can access it with my credentials (&lt;a href="mailto:turculaurentiu91@gmail.com"&gt;turculaurentiu91@gmail.com&lt;/a&gt; | password )&lt;br&gt;
Our aim is to convert this project into a modern web app by integrating &lt;a href="https://htmx.org/"&gt;HTMX&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Along the way, we'll delve into advanced techniques to introduce features like active search, infinite scrolling, and real-time updates for the page when new elements are created by other users.&lt;/p&gt;
&lt;h2&gt;
  
  
  why HTMX?
&lt;/h2&gt;

&lt;p&gt;Because I love it! Since I landed on their website and understood the simplicity it can bring on a web developer life I can't stop thinking how almost everything we do on the web with React (or other frameworks 🙂) can be ridiculously easy done with plain old HTML enhanced with HTMX.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Yes, you can achieve the same with HTTP API &amp;amp; React (or other frameworks 🙂).&lt;/li&gt;
&lt;li&gt;Yes, you can achieve the same with Inertia.js&lt;/li&gt;
&lt;li&gt;Yes, you can achieve the same with Livewire&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But this tutorial is about HTMX and how easy it is to use. Think of it as just another tool in your toolbox that you can use to replace or use alongside of the above mentioned technologies.&lt;/p&gt;

&lt;p&gt;This article is not about why you should use it, but how you can use it. But the gist of it is that is dead simple, and it keeps your apps simple. And simple is good!&lt;/p&gt;

&lt;p&gt;If you want read more about the why, you can take a look at this other &lt;a href="https://dev.to/turculaurentiu91/why-you-should-choose-htmx-for-your-next-project-o7j"&gt;article&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;In order to follow along, you need to have completed the Laravel Bootcamp Chirper app using the blade templates. If you are familiar with Laravel and Blade and prefer not to go through that tutorial, you can clone my version of the solution (which replicates all the code from the bootcamp) from here: &lt;a href="https://github.com/turculaurentiu91/chirper-htmx"&gt;GitHub Repository&lt;/a&gt;. Be sure to check out the &lt;code&gt;the-beginning&lt;/code&gt; branch, as the &lt;code&gt;main&lt;/code&gt; branch contains the final solution.&lt;/p&gt;

&lt;p&gt;The next step is to install HTMX. We can take two routes here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install it via npm, bundle it with Vite (a build tool that facilitates faster development).&lt;/li&gt;
&lt;li&gt;Load the script via CDN in the header of your template.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Usually, the first option is preferable as it allows HTMX to be served by your infrastructure, enabling you to take advantage of HTTP/2 multiplexed streams. However, for the sake of simplicity in this tutorial, we will add it directly in the templates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/layouts/app.blade.php --&amp;gt;&lt;/span&gt;

      &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/htmx.org@1.9.6"&lt;/span&gt; &lt;span class="na"&gt;integrity=&lt;/span&gt;&lt;span class="s"&gt;"sha384-FhXw7b6AlE/jyjlZH5iHa/tTe9EpJ1Y55RjcgPbjeWMskSxZt1v9qkxLJWNJaGni"&lt;/span&gt; &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, we just added the script in the head from unpkg. That's all, we can now go and use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Boosting the navigation
&lt;/h2&gt;

&lt;p&gt;If you open the application, login, and navigate between the Dashboard and the Chirps tab, you will notice that the whole page is reloaded with each navigation. This is where frameworks like React shine by enabling seamless navigation without page reloads. Now, let's see how we can achieve similar seamless navigation with HTMX, by taking advantage of the &lt;code&gt;hx-boost&lt;/code&gt; attribute:&lt;/p&gt;

&lt;p&gt;So let's see how we can achieve that with HTMX, by taking advantage of the &lt;code&gt;hx-boost&lt;/code&gt; attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/layouts/navigation.blade.php --&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- Navigation Links --&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hidden space-x-8 sm:-my-px sm:ml-10 sm:flex"&lt;/span&gt; &lt;span class="na"&gt;hx-boost=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;x-nav-link&lt;/span&gt; &lt;span class="na"&gt;:href=&lt;/span&gt;&lt;span class="s"&gt;"route('dashboard')"&lt;/span&gt; &lt;span class="na"&gt;:active=&lt;/span&gt;&lt;span class="s"&gt;"request()-&amp;gt;routeIs('dashboard')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
        {{ __('Dashboard') }}  
    &lt;span class="nt"&gt;&amp;lt;/x-nav-link&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;x-nav-link&lt;/span&gt; &lt;span class="na"&gt;:href=&lt;/span&gt;&lt;span class="s"&gt;"route('chirps.index')"&lt;/span&gt; &lt;span class="na"&gt;:active=&lt;/span&gt;&lt;span class="s"&gt;"request()-&amp;gt;routeIs('chirps.index')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
        {{ __('Chirps') }}  
    &lt;span class="nt"&gt;&amp;lt;/x-nav-link&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We added the &lt;code&gt;hx-boost="true"&lt;/code&gt; to the parent div of the navigation links. What HTMX will do behind the scene is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It will install an &lt;code&gt;onClick&lt;/code&gt;  event for the links.&lt;/li&gt;
&lt;li&gt;It will prevent the default browser navigation.&lt;/li&gt;
&lt;li&gt;It will do an AJAX request to the URL specified in the &lt;code&gt;href&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;It will fetch the HTML document.&lt;/li&gt;
&lt;li&gt;It will replace the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; of the document with the content received in the request.&lt;/li&gt;
&lt;li&gt;It will update the navigation URL to match the &lt;code&gt;href&lt;/code&gt; attribute of the clicked anchior.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, when you click on the navigation links, the whole page will no longer reload, demonstrating the power and simplicity of HTMX.&lt;/p&gt;

&lt;p&gt;HTMX attributes are inheritable, meaning that you can inherit attributes from ancestor elements. Note how we added the &lt;code&gt;hx-boost&lt;/code&gt; attr. on the parent div of the navigation links. We can, in fact, we will move the attribute on the wrapper div of the whole page.&lt;/p&gt;

&lt;p&gt;So we remove it from &lt;code&gt;resources/views/layouts/navigation.blade.php&lt;/code&gt; and add it on the app layout&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/layouts/app.blade.php --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"font-sans antialiased"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"min-h-screen bg-gray-100"&lt;/span&gt; &lt;span class="na"&gt;hx-boost=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- rest of the template --&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And so, we added the boost to all logged-in users. Now, whatever action the user takes—creating a chirp, editing, deleting, or interacting with any other forms—they will not trigger a whole page reload.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HOMEWORK:&lt;/strong&gt; add the boost to the guest users, so the auth forms has the same boost.&lt;/p&gt;

&lt;p&gt;Checkmate React, our job here is done. With one &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag and one HTML attribute we replaced react, react-router, redux, Axios and a couple of other js dependencies that you would install just because you can.&lt;/p&gt;

&lt;h2&gt;
  
  
  CSS Transitions
&lt;/h2&gt;

&lt;p&gt;You might notice that the navigation links have a CSS transition property, but it only works when you hover to display the underline. When you click, the underline just pops into existence, which can be jarring. Let's smooth that out!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/layouts/navigation.blade.php --&amp;gt;&lt;/span&gt;  
&lt;span class="c"&gt;&amp;lt;!-- Navigation Links --&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hidden space-x-8 sm:-my-px sm:ml-10 sm:flex"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;x-nav-link&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"dashboard-link"&lt;/span&gt; &lt;span class="na"&gt;:href=&lt;/span&gt;&lt;span class="s"&gt;"route('dashboard')"&lt;/span&gt; &lt;span class="na"&gt;:active=&lt;/span&gt;&lt;span class="s"&gt;"request()-&amp;gt;routeIs('dashboard')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
        {{ __('Dashboard') }}  
    &lt;span class="nt"&gt;&amp;lt;/x-nav-link&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;x-nav-link&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"chirps-link"&lt;/span&gt; &lt;span class="na"&gt;:href=&lt;/span&gt;&lt;span class="s"&gt;"route('chirps.index')"&lt;/span&gt; &lt;span class="na"&gt;:active=&lt;/span&gt;&lt;span class="s"&gt;"request()-&amp;gt;routeIs('chirps.index')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
        {{ __('Chirps') }}  
    &lt;span class="nt"&gt;&amp;lt;/x-nav-link&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- rest of the template --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, all we did was to add some ids on the links. &lt;/p&gt;

&lt;p&gt;To apply a CSS transition to swapped content, it's essential to keep its IDs consistent across requests. Under the hood, HTMX will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Swap the content,&lt;/li&gt;
&lt;li&gt;Match the elements by ID,&lt;/li&gt;
&lt;li&gt;Apply the existing properties (CSS classes in our case) to the new elements,&lt;/li&gt;
&lt;li&gt;And after a brief pause of a few milliseconds (20), apply the new properties to these elements.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This sequence allows the CSS transitions to function smoothly, providing a visual cue to users as the active navigation link changes. Now, when you click on the navigation links, the underline will transition smoothly instead of just popping into existence, enhancing the user experience with a more polished, professional look.&lt;/p&gt;

&lt;p&gt;By making this simple adjustment, we've improved the visual feedback to users as they navigate through the application, showcasing another aspect of the user-friendly enhancements HTMX can bring to a Laravel project. &lt;/p&gt;

&lt;p&gt;For more detailed information on how CSS transitions are handled with HTMX, feel free to check out the &lt;a href="https://htmx.org/docs/#css_transitions"&gt;HTMX documentation page on CSS transitions&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhancing the edit action
&lt;/h2&gt;

&lt;p&gt;Imagine your Product Owner comes around and mentions disliking the redirection to another page when editing a chirp. He prefer that upon clicking the edit button, the chirp morphs into a form. On saving, it reverts back to a regular chirp displaying the updated content. Let’s delve into how we can achieve this.&lt;/p&gt;

&lt;h3&gt;
  
  
  The edit button
&lt;/h3&gt;

&lt;p&gt;The goal is to convert the chirp into a form upon clicking the edit button. To do this, we'll create a Blade component that renders a chirp in edit mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/chirps/edit.blade.php --&amp;gt;&lt;/span&gt;

@props(['chirp'])  

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"this"&lt;/span&gt; &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; 
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"p-6 flex flex-col space-x-2"&lt;/span&gt; 
        &lt;span class="na"&gt;hx-patch=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.update', $chirp) }}"&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
        @csrf  
        &lt;span class="nt"&gt;&amp;lt;textarea&lt;/span&gt;  
            &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;  
            &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"block w-full border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            {{ old('message', $chirp-&amp;gt;message) }}
        &lt;span class="nt"&gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;lt;x-input-error&lt;/span&gt; &lt;span class="na"&gt;:messages=&lt;/span&gt;&lt;span class="s"&gt;"$errors-&amp;gt;get('message')"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-2"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-4 space-x-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;x-primary-button&amp;gt;&lt;/span&gt;{{ __('Save') }}&lt;span class="nt"&gt;&amp;lt;/x-primary-button&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"cursor-pointer"&lt;/span&gt; 
            &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.show', $chirp) }}"&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                {{ __('Cancel') }}
            &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;    
    &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we've created a new Blade component that renders the chirp form inline. The &lt;code&gt;hx-target&lt;/code&gt; and &lt;code&gt;hx-swap&lt;/code&gt; attributes on the wrapper div instruct HTMX to target this div (itself) and replace its &lt;code&gt;outerHTML&lt;/code&gt; with the response contents for all requests within this div, thanks to HTMX's attribute inheritance.&lt;/p&gt;

&lt;p&gt;On the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; element and the cancel link, we've specified the request methods to be &lt;strong&gt;PATCH&lt;/strong&gt; and &lt;strong&gt;GET&lt;/strong&gt; respectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating the Controller
&lt;/h3&gt;

&lt;p&gt;Now, let’s modify the &lt;code&gt;update&lt;/code&gt; method in our controller to respond with the partial view if it's an HTMX request, maintaining the normal browser functionality when JavaScript is disabled for a progressively enhanced website.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/Http/Controllers/ChirpController.php&lt;/span&gt;

 &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Chirp&lt;/span&gt; &lt;span class="nv"&gt;$chirp&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;RedirectResponse&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nc"&gt;Response&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'update'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$chirp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  

    &lt;span class="nv"&gt;$validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;  
        &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|string|max:255'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="p"&gt;]);&lt;/span&gt;  

    &lt;span class="nv"&gt;$chirp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$validated&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="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HX-Request'&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="nf"&gt;response&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;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'components.chirps.single'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
            &lt;span class="s1"&gt;'chirp'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$chirp&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="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps.index'&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 Single Chirp Component
&lt;/h3&gt;

&lt;p&gt;Now, let's discuss the single chirp component which we've extracted for reusability.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/components/chirps/single.blade.php --&amp;gt;&lt;/span&gt;

@props(['chirp'])  

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"p-6 flex space-x-2 chirp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; 
        &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; 
        &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-6 w-6 text-gray-600 -scale-x-100"&lt;/span&gt; 
        &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt; 
        &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt; 
        &lt;span class="na"&gt;stroke-width=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; 
            &lt;span class="na"&gt;stroke-linecap=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt; 
            &lt;span class="na"&gt;stroke-linejoin=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt; 
            &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;    
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex justify-between items-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;                
                &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-gray-800"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ $chirp-&amp;gt;user-&amp;gt;name }}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;  
                &lt;span class="nt"&gt;&amp;lt;small&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"ml-2 text-sm text-gray-600"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    {{ $chirp-&amp;gt;created_at-&amp;gt;format('j M Y, g:i a') }}
                &lt;span class="nt"&gt;&amp;lt;/small&amp;gt;&lt;/span&gt;  
                @unless ($chirp-&amp;gt;created_at-&amp;gt;eq($chirp-&amp;gt;updated_at))  
                    &lt;span class="nt"&gt;&amp;lt;small&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-sm text-gray-600"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 
                        &lt;span class="ni"&gt;&amp;amp;middot;&lt;/span&gt; {{ __('edited') }}
                    &lt;span class="nt"&gt;&amp;lt;/small&amp;gt;&lt;/span&gt;  
                @endunless  
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;  
            @if ($chirp-&amp;gt;user-&amp;gt;is(auth()-&amp;gt;user()))  
                &lt;span class="nt"&gt;&amp;lt;x-dropdown&amp;gt;&lt;/span&gt;  
                    &lt;span class="nt"&gt;&amp;lt;x-slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"trigger"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
                        &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;                            
                            &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; 
                                &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; 
                                &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-4 w-4 text-gray-400"&lt;/span&gt; 
                                &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 20 20"&lt;/span&gt; 
                                &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
                                &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"M6 10a2 2 0 11-4 0 2 2 0 014 0zM12 10a2 2 0 11-4 0 2 2 0 014 0zM16 12a2 2 0 100-4 2 2 0 000 4z"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;  
                            &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;                        
                        &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;                   
                    &lt;span class="nt"&gt;&amp;lt;/x-slot&amp;gt;&lt;/span&gt;                    
                    &lt;span class="nt"&gt;&amp;lt;x-slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
                        &lt;span class="nt"&gt;&amp;lt;x-dropdown-link&lt;/span&gt;                          
                            &lt;span class="na"&gt;:href=&lt;/span&gt;&lt;span class="s"&gt;"route('chirps.edit', $chirp)"&lt;/span&gt;  
                            &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.edit', $chirp) }}"&lt;/span&gt;  
                            &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"closest .chirp"&lt;/span&gt;  
                            &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;  
                        &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
                            {{ __('Edit') }}  
                        &lt;span class="nt"&gt;&amp;lt;/x-dropdown-link&amp;gt;&lt;/span&gt;  
                        &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; 
                            &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt; 
                            &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.destroy', $chirp) }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
                            @csrf  
                            @method('delete')  
                            &lt;span class="nt"&gt;&amp;lt;x-dropdown-link&lt;/span&gt; 
                                &lt;span class="na"&gt;:href=&lt;/span&gt;&lt;span class="s"&gt;"route('chirps.destroy', $chirp)"&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"event.preventDefault(); this.closest('form').submit();"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
                                {{ __('Delete') }}  
                            &lt;span class="nt"&gt;&amp;lt;/x-dropdown-link&amp;gt;&lt;/span&gt;  
                        &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;                    
                    &lt;span class="nt"&gt;&amp;lt;/x-slot&amp;gt;&lt;/span&gt;                
                &lt;span class="nt"&gt;&amp;lt;/x-dropdown&amp;gt;&lt;/span&gt;            
            @endif  
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;  
        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-4 text-lg text-gray-900"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ $chirp-&amp;gt;message }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've moved the chirp rendering code to a reusable Blade component. The HTMX attributes on the edit button ensure that a GET request is made to the edit endpoint, replacing the closest parent div with class &lt;code&gt;chirp&lt;/code&gt; with the response markup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Edit Chirp Partial/Component
&lt;/h3&gt;

&lt;p&gt;Ensure that HTMX requests to the edit chirp endpoint receive only the edit chirp component, while other requests receive the whole page, aligning with the "progressive enhancement" pattern.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/Http/Controllers/ChirpController.php&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Chirp&lt;/span&gt; &lt;span class="nv"&gt;$chirp&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'update'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$chirp&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="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HX-Request'&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="nf"&gt;response&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;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'components.chirps.edit'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
            &lt;span class="s1"&gt;'chirp'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$chirp&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="nf"&gt;response&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;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps.edit'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
        &lt;span class="s1"&gt;'chirp'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$chirp&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;Now, update the index layout to utilize the single chirp component and implement the &lt;code&gt;view&lt;/code&gt; endpoint of the single chirp.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/chirps/index.blade.php --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-6 bg-white shadow-sm rounded-lg divide-y"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    @foreach ($chirps as $chirp)  
        &lt;span class="nt"&gt;&amp;lt;x-chirps.single&lt;/span&gt; &lt;span class="na"&gt;:chirp=&lt;/span&gt;&lt;span class="s"&gt;"$chirp"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;  
    @endforeach  
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Chirp&lt;/span&gt; &lt;span class="nv"&gt;$chirp&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&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;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'components.chirps.single'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
        &lt;span class="s1"&gt;'chirp'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$chirp&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;Don't forget to add the new endpoint to &lt;code&gt;routes.php&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Review
&lt;/h3&gt;

&lt;p&gt;Let's review the flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clicking the edit button triggers a GET request to &lt;code&gt;chirps/{chirp}/edit&lt;/code&gt;, replacing the parent div with class &lt;code&gt;chirp&lt;/code&gt; with the response markup from the &lt;code&gt;components/chirps/edit&lt;/code&gt; partial template.&lt;/li&gt;
&lt;li&gt;Clicking the cancel link on the form triggers a GET request to &lt;code&gt;chirps/{chirp}&lt;/code&gt;, replacing the wrapper div element with the content of the response, i.e., the partial markup of the single chirp element.&lt;/li&gt;
&lt;li&gt;Submitting the edit form triggers a PATCH request to &lt;code&gt;chirps/{chirp}&lt;/code&gt; endpoint, updating the chirp and replacing the wrapper div element with the markup of the single chirp, the edited one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With the &lt;code&gt;HX-Request&lt;/code&gt; header, we've maintained normal browser functionality in case JavaScript is disabled, embodying the principle of progressive enhancement. Our application remains server-side rendered, fast, and simple, with a significantly enhanced user experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PS: Note that the validation isn't working currently; we'll address this later in the tutorial.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Through these steps, we've seamlessly integrated HTMX to enhance the edit action in our Laravel application, providing a more intuitive and interactive user experience without the need for complex front-end frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhancing the create action.
&lt;/h2&gt;

&lt;p&gt;The create action can be enhanced by making two key changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modifying the store action to return the single chirp during an HTMX request.&lt;/li&gt;
&lt;li&gt;Injecting the single chirp markup at the beginning of the chirps list.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s delve into these steps:&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating the Store Action
&lt;/h3&gt;

&lt;p&gt;Update the &lt;code&gt;store&lt;/code&gt; method in &lt;code&gt;ChirpController&lt;/code&gt; to return the partial view of a single chirp when an HTMX request is detected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/Http/Controllers/ChirpController.php&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;RedirectResponse&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nc"&gt;Response&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="nv"&gt;$validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;  
        &lt;span class="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|string|max:255'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
    &lt;span class="p"&gt;]);&lt;/span&gt;  

    &lt;span class="nv"&gt;$chirp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;user&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;chirps&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;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$validated&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="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HX-Request'&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="nf"&gt;response&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;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'components.chirps.single'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
            &lt;span class="s1"&gt;'chirp'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$chirp&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="nf"&gt;redirect&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;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps.index'&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;
  
  
  Modifying the Index Template
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;chirps.index&lt;/code&gt; template, update the form element by adding HTMX attributes, instructing it to insert the response at the top of the chirps list (&lt;code&gt;afterbegin&lt;/code&gt;). Also, add an ID to the chirps list div for targeting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt;  
    &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;  
    &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.store') }}"&lt;/span&gt;  
    &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.store') }}"&lt;/span&gt;  
    &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#chirps"&lt;/span&gt;  
    &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"afterbegin"&lt;/span&gt;  &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- rest of the template --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"chirps"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-6 bg-white shadow-sm rounded-lg divide-y"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling Form Reset
&lt;/h3&gt;

&lt;p&gt;You may notice that the textarea content remains unchanged even after a successful request. To enhance user experience, let's reset the form upon a successful submission using the &lt;code&gt;hx-on&lt;/code&gt; attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt;  
    &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;  
    &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.store') }}"&lt;/span&gt;  
    &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.store') }}"&lt;/span&gt;  
    &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#chirps"&lt;/span&gt;  
    &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"afterbegin"&lt;/span&gt;  
    &lt;span class="na"&gt;hx-on=&lt;/span&gt;&lt;span class="s"&gt;"htmx:afterRequest: if(event.detail.successful) this.reset();"&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we've used the &lt;code&gt;hx-on&lt;/code&gt; attribute to bind a script to the &lt;code&gt;htmx:afterRequest&lt;/code&gt; event, which HTMX triggers after each request. We check whether the request was successful using &lt;code&gt;event.detail.successful&lt;/code&gt;, and if so, we reset the form using &lt;code&gt;this.reset()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And there you have it! We’ve significantly enhanced the create method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note: The validation is not yet functioning correctly, but we'll address this issue later in the tutorial.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These steps demonstrate how we've seamlessly integrated HTMX to improve the create action in our Laravel application, providing a better user experience without adding complexity to the Front-end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Delete Action
&lt;/h2&gt;

&lt;p&gt;The delete action is relatively straightforward. We can respond with an empty string for a successful delete HTMX request, instructing HTMX to remove the item from the DOM. For non-HTMX requests, the behavior remains unchanged.&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating the Destroy Method
&lt;/h3&gt;

&lt;p&gt;Modify the &lt;code&gt;destroy&lt;/code&gt; method in &lt;code&gt;ChirpController&lt;/code&gt; to return an empty string during an HTMX request, and to redirect to the &lt;code&gt;chirps.index&lt;/code&gt; route otherwise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Chirp&lt;/span&gt; &lt;span class="nv"&gt;$chirp&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;RedirectResponse&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'delete'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$chirp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  

    &lt;span class="nv"&gt;$chirp&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;delete&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="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HX-Request'&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="s1"&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="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps.index'&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;
  
  
  Modifying the Dropdown-Link Component
&lt;/h3&gt;

&lt;p&gt;The current implementation in Laravel Bootcamp Chirper has a &lt;code&gt;&amp;lt;x-dropdown-link&amp;gt;&lt;/code&gt; component that always renders a link. To ensure it works even with JavaScript disabled, modify it to render a button instead, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/components/dropdown-link.blade.php --&amp;gt;&lt;/span&gt;

@props(['component' =&amp;gt; 'link'])  

@if ($component === 'link')  
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="err"&gt;{{&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="na"&gt;attributes-&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;merge(['class' =&amp;gt; 'block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out']) }}&amp;gt;{{ $slot }}&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;  
@else  
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="err"&gt;{{&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="na"&gt;attributes-&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;merge(['class' =&amp;gt; 'block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out']) }}&amp;gt;{{ $slot }}&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;  
@endif
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;here I shamelessly copied the classes around and not used a variable to be more DRY&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adjusting the Single Chirp Component
&lt;/h3&gt;

&lt;p&gt;Now, update the single chirp component to utilize the modified &lt;code&gt;&amp;lt;x-dropdown-link&amp;gt;&lt;/code&gt; and adjust the form to execute a delete request, removing the chirp from the DOM upon request completion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;
&lt;span class="c"&gt;&amp;lt;!-- resources/views/components/chirps/single.blade.php --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;  
      &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"{{ route('chirps.destroy', $chirp) }}"&lt;/span&gt;  
      &lt;span class="na"&gt;hx-delete=&lt;/span&gt;&lt;span class="s"&gt;"{{route('chirps.destroy', $chirp)}}"&lt;/span&gt;  
      &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"closest .chirp"&lt;/span&gt;  
      &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"delete"&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
    @csrf  
    @method('delete')  
    &lt;span class="nt"&gt;&amp;lt;x-dropdown-link&lt;/span&gt;  
        &lt;span class="na"&gt;:component=&lt;/span&gt;&lt;span class="s"&gt;"'button'"&lt;/span&gt;  
        &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
        {{ __('Delete') }}  
    &lt;span class="nt"&gt;&amp;lt;/x-dropdown-link&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, a submit button is rendered as a dropdown-link, and the form is adjusted to make a delete request, removing the chirp from the DOM upon request completion.&lt;/p&gt;

&lt;p&gt;While it's noted that the dropdown link now leverages Alpine.js and may not function without JavaScript, addressing this falls outside the scope of this tutorial. The focus here is on showcasing the HTMX integration to handle delete actions seamlessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Infinite Scrolling
&lt;/h2&gt;

&lt;p&gt;Let's first generate additional chirps using Laravel's factory and seeder functionality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan make:factory ChirpFactory &lt;span class="nt"&gt;--model&lt;/span&gt; Chirp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;  

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Database\Factories&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\Chirp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Database\Eloquent\Factories\Factory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  

&lt;span class="cd"&gt;/**  
 * @extends Factory&amp;lt;Chirp&amp;gt;  
 */&lt;/span&gt;  
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChirpFactory&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Factory&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="cd"&gt;/**  
     * Define the model's default state.     
     *     
     * @return array&amp;lt;string, mixed&amp;gt;  
     */&lt;/span&gt;    
     &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;definition&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;  
    &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="nv"&gt;$created_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;dateTimeBetween&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-2 months'&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="s1"&gt;'message'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
            &lt;span class="s1"&gt;'created_at'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="cm"&gt;/* somewhere in the past two months */&lt;/span&gt; &lt;span class="nv"&gt;$created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
            &lt;span class="s1"&gt;'updated_at'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;dateTimeBetween&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// DatabaseSeeder.php&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&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;hasChirps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&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;create&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;Now, run the seed command and go and grab a cup of coffee, this will take a while, we are generating 20 users with 2000 chirps each, so it will take a while.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php aritsan db:seed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Implementing Basic Pagination
&lt;/h3&gt;

&lt;p&gt;To prevent page overload, implement basic pagination in the &lt;code&gt;ChirpController&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/Http/Controllers/ChirpController.php&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="nv"&gt;$chirps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Chirp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user'&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;latest&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;paginate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;response&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;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chirps.index'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;  
        &lt;span class="s1"&gt;'chirps'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$chirps&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;Display pagination links in &lt;code&gt;chirps.index&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/chirps/index.blade.php --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;noscript&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"max-w-5xl mx-auto p-4 sm:p-6 lg:p-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
        {{ $chirps-&amp;gt;links() }}  
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;/noscript&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding Infinite Scrolling
&lt;/h3&gt;

&lt;p&gt;Utilize HTMX to implement infinite scrolling while retaining the pagination links as a fallback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/chirps/index.blade.php --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"max-w-2xl mx-auto p-4 sm:p-6 lg:p-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
        &lt;span class="c"&gt;&amp;lt;!-- rest of the template --&amp;gt;&lt;/span&gt;

        @foreach ($chirps as $chirp)  
            &lt;span class="nt"&gt;&amp;lt;x-chirps.single&lt;/span&gt; &lt;span class="na"&gt;:chirp=&lt;/span&gt;&lt;span class="s"&gt;"$chirp"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;  
        @endforeach  

        @if($chirps-&amp;gt;nextPageUrl())  
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt;  
                &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"{{ $chirps-&amp;gt;nextPageUrl() }}"&lt;/span&gt;  
                &lt;span class="na"&gt;hx-select=&lt;/span&gt;&lt;span class="s"&gt;"#chirps&amp;gt;div"&lt;/span&gt;  
                &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;  
                &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"intersect"&lt;/span&gt;  
            &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
                Loading more...  
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;  
        @endif  
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;  

&lt;span class="nt"&gt;&amp;lt;noscript&amp;gt;&lt;/span&gt;  
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"max-w-5xl mx-auto p-4 sm:p-6 lg:p-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
        {{ $chirps-&amp;gt;links() }}  
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;/noscript&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure &lt;code&gt;x-cloak&lt;/code&gt; elements are hidden initially:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- resources/views/layouts/app.blade.php --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;  
    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;x-cloak&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;It's called critical because it's loaded before the CSS file is downloaded, that's why you put it in the header and not in the CSS file&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The HTMX attributes used in the infinite scrolling code snippet provide the necessary instructions to the browser on how to handle the loading of additional chirps as the user scrolls down the page. Let's break down these attributes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;hx-get="{{ $chirps-&amp;gt;nextPageUrl() }}"&lt;/code&gt;: This attribute instructs HTMX to issue a GET request to the URL specified, which in this case is the URL for the next page of chirps. This action is triggered when the user scrolls down and reaches the "Loading more..." div.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hx-select="#chirps&amp;gt;div"&lt;/code&gt;: This attribute specifies what part of the returned content from the GET request should be used in the current page. In this case, it's selecting all direct &lt;code&gt;div&lt;/code&gt; children of the element with the id &lt;code&gt;#chirps&lt;/code&gt;. This will essentially select all the chirps returned in the next page of results.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hx-swap="outerHTML"&lt;/code&gt;: This attribute specifies how to update the DOM with the selected elements from the GET request. The value &lt;code&gt;outerHTML&lt;/code&gt; instructs HTMX to replace the "Loading more..." div entirely with the new content fetched from the server. This way, the newly loaded chirps get appended to the list, and the "Loading more..." div gets replaced by these new chirps.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hx-trigger="intersect"&lt;/code&gt;: This attribute specifies when to trigger the GET request. The value &lt;code&gt;intersect&lt;/code&gt; tells HTMX to trigger the request when the "Loading more..." div comes into view (i.e., when it intersects with the viewport). This is essentially what enables the infinite scrolling behavior, as new chirps are loaded each time the user scrolls down and reaches the end of the current list.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;x-cloak&lt;/code&gt;: This is an Alpine.js directive, not an HTMX attribute. It's used to hide certain elements initially. When JavaScript is enabled, Alpine.js will remove the &lt;code&gt;x-cloak&lt;/code&gt; attribute, making the element visible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By utilizing these HTMX attributes and the Alpine.js directive, the code is set up to provide a seamless infinite scrolling experience while also having a fallback pagination system for users with JavaScript disabled. This way, the application maintains a good level of accessibility and usability across different user scenarios. How cool is that ?!?!?!&lt;/p&gt;

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

&lt;p&gt;Throughout this tutorial, we've journeyed through enhancing a basic Laravel chirps application with progressively enhanced features utilizing HTMX. We started with simple refinements to form submissions, worked through editing and deleting chirps inline, and finally, implemented infinite scrolling to efficiently load and display a large number of chirps. Each enhancement aimed to provide a more interactive and user-friendly experience while retaining functionality without JavaScript, adhering to the principles of Progressive Enhancement.&lt;/p&gt;

&lt;p&gt;The simplicity and power of HTMX shone through, allowing us to keep our code largely server-side, clean, and maintainable, while still offering a dynamic user experience. The transition from traditional server-rendered interactions to a more dynamic interface didn't require a complete rewrite or the introduction of complex client-side frameworks. This stands as a testament to how modern technologies can simplify the development process while adhering to good practices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Up Next: Dive Deeper into Interactive Features
&lt;/h2&gt;

&lt;p&gt;In the upcoming second part of this tutorial, we'll take our application to the next level by introducing more interactive and real-time features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spinners:&lt;/strong&gt; Enhance the user experience by adding the loading indicator&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active Search:&lt;/strong&gt; We'll explore how to implement an active search feature using MySQL full-text indexes to help users find chirps that interest them quickly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-Side Validation:&lt;/strong&gt; Addressing a loose end, we'll work on fixing the server-side validation to ensure data integrity and provide user feedback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-Time Updates:&lt;/strong&gt; Using the pooling method, we'll update our index page with new chirps in real-time, keeping the content fresh and engaging for our users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Experiment with Server-Side Events:&lt;/strong&gt; We'll take a shot at implementing server-side events to further enhance the real-time interactivity of our application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The adventure continues as we delve deeper into creating a more interactive and real-time experience without sacrificing the simplicity and maintainability of our Laravel application. Stay tuned, and get ready to explore more ways to bring your Laravel applications to life!&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>htmx</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
