00:00:05.339
Hey everyone, welcome to my RailsConf talk called "Frontendless Rails Frontend." My name is Vladimir, and I work for a company called Evil Martians.
00:00:11.820
As a backend engineer, at least from the legal point of view, I’m mostly working with Ruby and a bit of Golang these days. I’m not dealing with front-end at work at all because web development today has a clear separation between backend and frontend engineers. Everyone does their job and does it well, but that’s what we have today.
00:00:27.599
Most of you, I think, might remember that ten or even five years ago, when I started working on Ruby on Rails projects, I was involved in everything: HTML, Ruby, JavaScript. Oh, actually CoffeeScript. I was able to build a product alone; I didn't need anyone else. I miss those times a little bit.
00:01:00.000
Today, I'd like to raise and try to answer the following question: could Rails full stack development be a thing in the 2020s? Let’s start with another question: what is full stack?
00:01:19.500
One definition is that there is a single person who can do everything, but I doubt that's a correct definition for full stack development. Instead, I'd like to say that full stack development is when you develop within a single ecosystem, and in our case, that ecosystem is Rails.
00:01:36.720
Let’s take a quick look at the past and remind ourselves how our ecosystem looked like when we were building Rails applications without needing to learn front-end frameworks and all that modern stuff. Also, let’s explore what happened to that way of building web applications.
00:02:00.780
First of all, the core principle of Rails full stack development is "HTML Over The Wire." The server is the only source of truth and is responsible for what end users see—so it’s power to the server. That’s how we can describe this way of building layers of web applications.
00:02:17.280
So let’s take a look at what tools we used to build the front-end part of our apps. First of all, we had HTML preprocessors to make it easier to write HTML, and we used Ruby helpers to craft HTML using Ruby code. Do you remember helpers? Something like that? Can you figure out what’s going on here? I can’t because, well... I don’t understand what’s written. Hopefully, we no longer write such things nowadays; at least we don’t have to.
00:02:51.300
Then, we added a bit of interaction by writing some JavaScript, or more precisely, CoffeeScript, because it was kind of JavaScript for Rubyists. We usually used jQuery and some plugins, for example, to handle AJAX requests and obtrusive JavaScript, which made it simple to turn links and forms into asynchronous communication tools—like performing an AJAX request instead of reloading pages.
00:03:27.599
We were also able to execute JavaScript in response to events, which felt like some kind of magic that Rails provided. Then we had Turbolinks, but no one knew how to use them properly except for perhaps Basecamp. As for styles, we used Sass to manage styles because it was much more Ruby-friendly than plain CSS.
00:04:14.099
Finally, we used Bootstrap to make all websites look alike, managing dependencies by installing gems using our good old Bundler. I’d simply put scripts into the vendor/assets folder. At the top of this iceberg, we had the asset pipeline and, more precisely, the Sprockets library, to take care of all this stuff: to build them together, optimize them, and minimize whatever was necessary. Those asset pipelines were the best for this job among all web frameworks, not just Ruby frameworks.
00:05:01.759
But things have changed. JavaScript ecosystems evolved, and the evolution began. First, npm appeared, and later Yarn, solving the problem of managing dependencies. Then JavaScript standards, such as ECMAScript, evolved to newer versions and made CoffeeScript less relevant. Webpack took asset compilation to the next level, and various tools built on top of it changed the way we write CSS, making it more powerful.
00:05:47.880
After that, front-end frameworks appeared, and React won the battle among them, becoming the number one even today. Finally, most applications transitioned into single-page applications, leaving Rails with the single responsibility of providing APIs. That’s where we are today: we have separate teams, larger development costs in terms of both time and money. That’s something we’d like to change—bringing back our Rails comfort zone.
00:06:56.100
And the question is whether it’s possible at all. Of course, it is, because we have examples like GitHub and Basecamp. They’ve been following the classic Rails way for many years. You might say those two are too old and too big to migrate to a modern front-end stack and that it would be too expensive for them. I get it.
00:07:34.560
But what about something that was just released about a year ago? Hey, we can use Rails as a core stack with just a couple of things to build the modern client-side experience. This new magic is called Hotwire, which brings the HTML-over-the-wire approach to the next level.
00:08:10.740
However, I'm not going to discuss Hotwire today because we have three or maybe four talks dedicated to it in the program, and even one workshop. Instead, I wrote a blog post that you can check later if you want to know my thoughts about Hotwire. So let’s move to the main part of the talk.
00:08:34.920
What I want to show is how the HTML-over-the-wire approach could evolve with modern front-end technologies. Let’s consider the scale on both sides and start with the core principles: who is responsible for rendering the UI? The server or the client?
00:09:01.340
One approach might be better than the other, but they are just two ways of doing the same thing and they're equally viable. Then we have single-page applications on the right—this is how most front-end-based applications are built.
00:09:28.440
How can we counter this? Turbolinks, or as we call it today, Turbo Drive, is the answer. Let’s recall what Turbolinks (or Turbo Drive) is. Turbo Drive allows for converting any HTML-based application into a single-page application by intercepting navigation events like link clicks and form submissions. Instead of reloading the page, it performs an AJAX request and replaces only the body with the response.
00:10:11.100
Now, scripts and styles are not reloaded for every navigation event, which makes loading pages faster, and it also keeps track of its own cache to enhance perceived performance for the end user.
00:10:55.200
Turbo Frames is the latest addition to the Turbo ecosystem in Hotwire, allowing us to reload just part of a page instead of the whole thing, which speeds up the first rendering. Let’s look at an example of using Turbo Frames—here we can see a to-do list app with items and the ability to mark them as completed or delete them, built with Turbo Frames and no JavaScript.
00:11:41.160
At the controller level, we only need to render partials instead of templates, and we wrap the contents in a special Turbo Frame tag with a unique identifier. This allows Turbo Frame JavaScript to replace the content of the element currently on the page or remove it completely if the contents of the Turbo Frame are empty. That’s it—just a couple of minor changes, and we can write interactive applications with no JavaScript!
00:13:00.120
Next, we have front-end frameworks that bring a lot of interactivity, reactivity, architecture patterns, concepts, and conventions. However, with only HTML and an HTML-first approach, we can add only simple JavaScript functionalities to enhance our HTML, like mentioned obtrusive JavaScript, but it is limited in functionality.
00:13:47.379
We need something more, and that’s where Stimulus comes in. Let’s talk a bit about Stimulus JS and why it’s a good companion for Rails development. For example, if we have hideable banners in the application that show tips to our users, the user can close them by clicking on the corresponding button. A decade ago, we’d have used jQuery to write this functionality, dealing with unrelated events and CSS classes, along with libraries like Turbolinks and jQuery UJS to ensure patterns worked properly.
00:14:40.579
However, with Stimulus, we focus only on what matters. Our JavaScript code is concise; we just need one function to implement the removal of the element, and everything else is controlled by the HTML using data attributes to activate this functionality in Stimulus.
00:15:50.100
The main benefit of Stimulus is its usage of modern browser APIs, such as the Mutation Observer API, which eliminates the need for tracking changes like we had to with jQuery and Turbolinks. Stimulus plays well with the server because you don't have to worry about any hacks to track loading or reloading. It allows you to attach behavior to an HTML element, turning it into a component. You implement the behavior itself in JavaScript by writing a controller.
00:16:39.000
Now, imagine building something more complex like a date picker or a search autocomplete input. We can write such components from scratch using JavaScript, but that would turn us into JavaScript developers. Alternatively, we can borrow existing components from some framework, such as React or Vue, and wrap them using Stimulus locally, thereby isolating everything.
00:17:43.200
For one of the projects, I started using Stimulus for interactive forms with dynamic fields, and I chose to use Vue for that and employed Stimulus to manage the mounting of Vue components into the HTML. The server still handles rendering and crafting HTML, and we continue utilizing Rails form helpers; we only add data attributes to activate them.
00:18:52.020
In this instance, the view component remains isolated. We do not introduce any state management or complex operations into our client-side code, keeping it simple. Stimulus simply helps manage external components' lifecycle. You can find a complete example in the open-source demo project we published to showcase this approach.
00:19:56.220
Let’s take a quick break.
00:20:00.000
I think I just talked too much JavaScript for a moment, so let’s go back to HTML and Ruby and our scale. The next thing is reactivity. Let’s define reactivity as an application's ability to react instantly to various events—both internal actions from users and external stimuli from the system.
00:21:07.740
In other words, users don’t need to hit the reload button to see the latest state of the data. How can we accomplish this without keeping state on the client when everything is on the server? We can push changes to clients through WebSockets.
00:21:59.880
Here, we can introduce an evolution of the HTML-over-the-wire approach, which is HTML over WebSocket. I’ll mention Phoenix LiveView, which popularized HTML over WebSocket, inspiring others to create similar solutions.
00:22:37.440
I won’t go into detail about how LiveView works; I've put the main ideas on the slide, so feel free to check them later. Let’s move on to projects inspired by it within the Ruby and Rails worlds. One such project is Stimulus Reflex.
00:23:09.420
Technically, Stimulus Reflex is a bit older than LiveView and its core part, CableReady, has been around for over three years. However, only after LiveView appeared and garnered hype did the development behind Stimulus Reflex get some momentum.
00:24:16.440
Let’s first look at CableReady: it's a small, single-purpose library that helps send HTML or DOM manipulation instructions from the server to the browser, enabling remote DOM manipulation controlled by the server. It reduces the actions Cable offers as a transfer and marks the DOM library on the client-side to update HTML as efficiently as possible.
00:25:22.620
For example, in our to-do list app, when we want to delete an item, there are no changes required in the HTML part of the application. We still use our remote call by clicking the delete button, but instead of returning HTML in our response, we broadcast a remove command to the corresponding stream with a specified selector for the current item.
00:26:11.220
This command could actually be expressed as a jQuery example from ten years ago. So, removing an object query requires no JavaScript; everything is done through CableReady. It not only supports removal but also over thirty commands, allowing extensive client-side manipulations to be executed from the server.
00:26:50.220
Now let’s look at Stimulus Reflex. It consists of two parts: Reflexes, which describe how to handle user actions, and CableReady to deliver HTML and commands, updating the DOM. The whole architecture can seem a bit complicated, so I recommend taking a look at an example. Again, our example stays consistent, but now it updates in real-time for all users—to make it reactive.
00:27:21.840
With the HTML, we only need to add data-reflex attributes, which resembles what we did with Stimulus. As indicated by the name, Stimulus Reflex uses Stimulus internally to activate reflexes, rendering the reflex attributes actionable. We need to write a class—in our case, the list reflex—implementing the corresponding methods.
00:28:41.400
Each method can perform multiple tasks. For instance, we can use CableReady as before and send custom modification commands responding specifically to the current user. In the example, we show the flash notification by broadcasting a partial HTML with a selector to replace contents. The community around Stimulus Reflex is majorly stable, well-documented, and very welcoming.
00:29:31.200
I find it a joy to work with and the people behind it. Moreover, it works seamlessly with any cable, which adds to its appeal. Stimulus Reflex isn’t the only framework adopting the HTML-over-the-wire ideology; I also want to highlight a couple more projects.
00:30:20.460
First, there's Motion, which offers a one-to-one correspondence between server-side components and DOM elements, making it an interesting project that's still evolving. Then we return to Turbo Streams, which I mentioned earlier from Hotwire—Turbo Streams are essentially like CableReady but with five factions, and they can be initiated with zero JavaScript.
00:31:36.120
To start using Turbo Streams, all you need is to add a specific element using a helper on the page to subscribe to the stream. Subsequently, you can send a similar DOM modification command like add, replace, or remove from your controller. This is again similar to CableReady but serves more in the Rails context.
00:32:39.900
We can see how the server and HTML over the wire (or over the socket) work effectively together. But now we face another challenge: we have a lot of HTML, a lot of partials for different scenarios, and we need to keep them organized.
00:33:16.260
Do we have any architecture patterns in Rails for managing HTML templates, like we see in front-end frameworks? I’m not sure, so maybe we should take inspiration from them. As a few years ago, Evil Martians demonstrated our Evil Front methodology to manage templates, heavily inspired by one of the main React principles: thinking in components.
00:33:44.940
Components are the answer, and now we have them in Rails applications through ViewComponents. Let me show you an example of using the ViewComponent gem by GitHub.
00:34:01.680
A ViewComponent consists of several parts. The first is a Ruby class that represents the necessary logic, functioning as a mix of presenters, helpers, decorators, or even page objects. It contains all the logic you need to draw a template.
00:34:51.840
Then there’s the template itself. Here’s how you use it in pure HTML templates: you render the component, providing the appropriate data. This keeps it isolated from the main rendering context, while also making it reusable as just a Ruby class, allowing you to apply inheritance and isolate testing.
00:35:42.240
Moreover, ViewComponent permits you to keep all related files together, not only Ruby logic and HTML representation, but also styles and JavaScript needed to activate this controller. Additionally, you can integrate Stimulus controllers, reflexes, and previews in a single folder—this isn’t possible out of the box, but you can extend ViewComponent to allow it.
00:36:24.840
Furthermore, you can eliminate repeated initializers and attributes in your code. I prefer keeping my ViewComponents clean and simple. Some developers even created a gem called ViewComponent Cantrip, which provides useful hacks to make writing ViewComponents more convenient than the default approach.
00:37:23.580
ViewComponent is not the only library that addresses this issue; various other libraries provide a similar ViewComponents approach for Rails that you can explore.
00:38:02.400
Now, we’ve compared interactivity, organization, architecture, and all that fun, but we’ve totally overlooked CSS. Another strength of front-end frameworks is the ability to leverage CSS-in-JS approaches, and in Rails, we still have CSS frameworks available.
00:38:53.860
We have CSS frameworks like Bootstrap that can be used but there are many other options available today.
00:39:06.420
I want to highlight three of them that I really liked during my work in production projects. The first is Bulma, a CSS-only mobile-first framework that I've already used along with Stimulus and Vue. The examples you saw earlier were built with Bulma, and it's great for rapid development.
00:39:55.140
Another framework is Tailwind CSS. I’m sure many of you have heard of this utility-first CSS framework that has taken the front-end world by storm. It doesn’t provide components out of the box, though Tailwind UI offers a component library.
00:40:13.980
Instead, it offers utility classes to allow you to write styles quickly and efficiently, making it ideal for prototyping while also enabling optimized builds through its integrations. I appreciate Tailwind's playground functionality, which allows you to create markup in the browser and then transfer it to your projects.
00:41:09.780
Finally, let me mention Shoelace—a library focused on customizability and accessibility that provides web components built with both CSS and JavaScript. It offers typical UI elements like alerts, forms, buttons, and dropdowns, making it very powerful for building internal tools or admin dashboards.
00:41:43.200
But again, CSS frameworks work well; the real question is how we can write isolated styles. Although we don’t have CSS-in-JS in Ruby on Rails, we can utilize PostCSS to help manage our styles effectively. It plays nicely with ViewComponents, and let me demonstrate how I bind them together.
00:42:40.860
For example, if we have a component called Flash Alert, we can keep the CSS and JavaScript within that component. In our CSS file, we write classes without worrying about specificity, since PostCSS will automatically ensure uniqueness.
00:43:16.920
Utilizing the appropriate helper classes in the HTML template, we resolve these internal names to global names that are made unique through our conventions.
00:43:48.540
The resulting HTML looks like this: instead of using generic class names like 'container' or 'body,' we have unique identifiers like 'C-flash-alert-container' and 'C-flash-alert-body.' This way, we maintain clear and non-conflicting styles in our Rails app.
00:44:16.880
In summary, I don't have anything to put on the scale, so let's discuss the outcome. We've reached the end of the talk, and I hope I was able to demonstrate that the HTML-first approach could be competitive. It makes sense to utilize this methodology in various contexts, proving that the Timeless way exists.
00:44:57.920
As someone who actively applies these practices in production and side projects, such as tools and applications, I can assure you that it allows for increased productivity while not depending on the cooperation of other engineers when it's not necessary.
00:45:43.920
You don’t always need a front-end engineer to create yet another form or admin interface. With Rails and the tools I mentioned, you can build visually appealing, interactive applications.
00:46:02.040
We’re just at the beginning of this new era, a Renaissance of Rails full-stack development, so stay tuned, join the movement, and thank you for your attention!