Hotwire in rails
In this section, talk about Hotwire. Hotwire is known as HTML over wire. In this I have explained what hotwire is, how it works, which are components of hotwire and how integration works.
What Is Hotwire?
To build a modern web application that is similar as a single-page application (SPA) and speeds up page loading, for that you send HTML instead of JSON data over the wire without using JavaScript.
Why would it be useful?
If you want to change a page’s content in real time, add JavaScript, wait for a response, and then change the content on the page. This process takes longer, we used hotwire to overcome JavaScript.
Rendering numerous partials, which slows down the application. Hotwire utilised to speed up page load time for it.
Hotwire Components
We have three components of hotwire. One is turbo, second is stimulus and last one is strada. Let’s understand what they are.
Turbo: The heart of hotwire is turbo. Turbo is an alternative to the turbo link. It’s work on server-side to send HTML to Web-browser by WebSocket.
Turbo is a collection of independent techniques for speeding up form submissions with partial page modifications over WebSocket. Also, speeding up page navigation and breaking down complex pages into manageable pieces.
It is divided into techniques such as turbo_stream, turbo_frame, turbo_drive, turbo_native.
- Turbo_frame: Allowing to modify specific section of page. Additionally, Without refreshing the page or typing a single line of JavaScript code, you can append, prepend, and remove page contents.
- Turbo_stream: it is a request-response format which is introduced by rails 7. Specify the action that will be performed along with the target ID of the DOM element that will be modified.
For example, where action=”replace” and target=”xyz” this would be replaced by a new HTML element where id=”xyz”. Same with other actions such as prepend, append and so on. - Turbo_drive: Approach is to speed up overall page-level navigation. It monitors for link clicks and form submissions, handles them in the background, and modifies the page without a full reload.
- Turbo_native: Used for mobile IOS & android development.
Stimulus: A simple JavaScript framework for HTML is called Stimulus. it’s developing client-side technologies that handles common JavaScript patterns fast instead of integrating front-end technologies to the view layer, such as vue, react, etc. Patterns similar to “the DOM is modified in response of an event occur”. Events such as click, blur and so on.
Strada: It enables HTML-based communication between a web application and a mobile application. Not have too much information about it.
Integration
In this article, we talked about turbo. Let’s use an example to make it clear. I created an app for storybooks.
rails new story-book
We have a simplistic CRUD operations for story-book application. It will redirect or re-render the page where we have made a POST/GET/PATCH request.
Without the use of Hotwire, the following would happen
With each request, a page will be reloaded and it’s working fine. But to add a little more excitement we’ll utilise TurboFrame for not to redirect or re-render a page. Additionally, we’ll utilise TurboStreams for forms.
Let’s now include hotwire in the application for storybooks.
gem "hotwire-rails"
After including the gem in the gemfile. To install the hot-wire gem, use the below command.
bundle install
Then execute the following command to install Hotwire.
rails hotwire:install
This includes stimulus rails, which inserts stimulus JS into the assert pipeline so we can build controllers without the use of Buble, webpack (which has packs/application.js), and turbo rails, a new version of turbo link that handles forms and links into applications.
Additionally, it’ll add Redis for you. Turbo utilises Redis as its data storage and action cable to manage WebSockets.
The application after including turbo-frame and turbo-stream.
TurboFrame:
Turbo Frames are standalone web page elements that don’t require coding any JavaScript and can be added to, prepended to, replaced with, or removed from without requiring a complete page refresh!
The create and edit forms will be added to the index page rather than redirecting to new different pages as they currently do in our application when inserting and modifying data, respectively.
To reduce page rendering and redirection, let’s use a turbo frame.
We utilise the turbo_frame_tag helper to generate TurboFrames. We have wrapped the link with an id of “create_story_form” to create a new story on the stories#index page, which redirects to the stories#new page.
After a browser refresh, the HTML elements look as follows:
The first argument of the turbo_frame_tag helper is a unique id. The button will still direct you to the stories#new page once you click it. Additionally, enter a warning on the console.
When a link within a TurboFrame is clicked and there isn’t a TurboFrame with the same id on the target page, the frame disappears and a warning is logged in the console as no matching turbo-frame id=”name of the frame” element.
Turbo expects a frame with the same id on the target page, whenever a link within a TurboFrame is clicked. The content of the Frame on the target page will immediately take the place of the Frame’s content on the source page.
Let’s now insert a target id into app/views/stories/new.html.erb for story form.
The “Create Story” button now displays a form on the stories#index page when clicked.
For the edit page, let’s add a turbo_frame_tag helper. Here, all stories are displayed by partially render app/views/stories/_story.html.erb; the story’s section is then wrapped by adding the story’s id to the turbo_frame_tag helper.
Add turbo_frame_tag to the target form on the app/views/stories/edit.html.erb as well. Which appears below
This edit form is now visible on the stories#index page.
Let’s try our code in the browser now that only those four lines of code have been inserted. Let’s select “Edit” to change a story. The story card is successfully replaced with the edit form.
Let’s submit the creation or modification form of the story. As you can see in the console, the form’s request is sent in turbo_stream format.
TurboStream Format:
Rails introduces the turbo_stream format, during database operations such as create, update, delete. The TurboStream technique can be used to perform actions such as append, prepend, update, replace, and remove.
The respond_to method allows controllers to handle TURBO_STREAM formats. This can be used in a variety of ways, including broadcasting from the model, turbo_stream.erb, and rendering from the controller.
Here, I’ve tried to manage turbo_stream requests in a variety of ways. I utilised broadcasting for the create action, turbo_stream render into the controller for the update action, and used turbo_stream.erb file for the destroy action.
Let’s look into it how we can utilised broadcasting from the model, turbo stream.erb, and rendering from the controller.
Via broadcasting:
Think about it, the first request will modify a page content if the database is changed. Other users either can’t see changes at once or must reload the page to see the most recent database changes.
We used broadcasting to show database changes simultaneously to other users. It will be utilised for transaction callbacks once any successful database actions performed, such as create, update, and destroy, have been completed.
In your model, the fundamental syntax for broadcasting data which inserted is as follows:
# app/models/story.rb
after_create_commit { broadcast_prepend_to 'stories' }
Here, the newly created stories are broadcast to the stories channel using a callback that is triggered each time a new story is created. This type of streaming requires the setup of a WebSocket connection to the channel that will receive the updates.
To achieve this, add the following code to app/views/stories/index.html.erb
<%= turbo_stream_from 'stories' %>
Currently, the app/views/stories/index.html.erb file looks like the one below.
In order to prevent updates from being lost in space, the channel name given to turbo_stream_from and broadcast action must match. In this case, the channel name is “stories”.
As was previously mentioned, actions like append, prepend, remove, etc. can also be used with broadcast. On the index page, it will prepend the newly created story.
Via render into controller:
Here, we use turbo_stream responses format into the controller to render a template using actions like replace and append and so on based on how it appears on a web page when modifying database data.
Here, without refreshing the website, the user interface will reflect with those stories which are updated into the database. The card div will be swapped out for an updated story.
Via turbo_stream.erb
While using turbo_frame helper, the request is sent as turbo_stream format when submitting a form. Therefore, responses must be sent back in the same turbo_stream format. Therefore, we created a turbo_stream.erb file using the response_to block in this.
A JS request has the same idea as this one. If we send a JS request, we either render data in JS format from the controller or create a js.erb file as a response.
Introducing a turbo_stream.erb file for the destroy action.
Build a destroy.turbo_stream.erb file to send a response.
# app/views/stories/destroy.turbo_stream.erb
<%= turbo_stream.remove "story_#{@story.id}" %>
By doing this, a particular story will be removed from the card div section without having to refresh the website.
I’ll try to include a topic of stimulus in my next post🤞. So if you like reading this, please like and share. And if you have any questions, don’t be afraid to get in touch with me. I appreciate you taking the time to read this guide🙌.