RailsConf 2021

Beautiful reactive web UIs, Ruby and you

Beautiful reactive web UIs, Ruby and you

by Jonas Jabari

In the talk "Beautiful Reactive Web UIs, Ruby and You" presented by Jonas Jabari at RailsConf 2021, the speaker explores developing reactive web interfaces using Ruby, showcasing a shift away from JavaScript-heavy approaches. He examines the evolution of UI development within the Rails framework and introduces a library he created, named Mate Stack, which allows developers to build reactive UIs solely in Ruby, enhancing developer happiness without sacrificing functionality.

Key Points Discussed:
- The classification of UI development approaches: the spectrum from low UI and developer happiness to beautiful, reactive UIs that can boost developer satisfaction.
- Traditional Rails views (ERB, Slim) and the challenges of integrating JavaScript, leading to complex state management and unhappy developers.
- Introduction of Hotwire and Stimulus Reflex as tools that help reduce JavaScript in favor of Rails, yet the speaker advocates for even more Ruby-centric solutions.
- The concept of Mate Stack, which completely abstracts UI creation using Ruby code, removing the need for HTML and JavaScript while leveraging Ruby's expressive syntax.
- The structure of Mate Stack components: creating UI components, routing, handling dynamic data without page reloads, and integrating with existing Rails applications seamlessly.
- A live demonstration using pre-built reactive components that mimic functions often requiring JavaScript, such as form submissions, updates, and real-time data syncing among connected clients.
- Pre-built styled components based on Bootstrap allow for faster UI development with minimal writing of HTML or CSS, enhancing productivity.
- Encouragement for developers to contribute to the open-source project, fostering a collaborative community around Mate Stack.

Conclusions and Takeaways:
- Mate Stack enables Rails developers to create beautiful, reactive UIs without delving deep into JavaScript, thus maintaining a focus on Ruby, which many developers favor for its syntax and object-oriented capabilities.
- The library aims to free developers from the complexity of front-end coding, allowing them to build intuitive and responsive applications while enjoying the elegance of Ruby.
- Developers are encouraged to explore, contribute to, and benefit from this open-source project, aimed at enhancing the Rails ecosystem and developer experience.

00:00:05.359 Hello everyone! I'm glad you decided to watch my talk about beautiful reactive web UIs with Ruby.
00:00:11.780 Let's jump right into the topic and review the state of UI development for Rails developers in 2021 from a Rails developer's perspective.
00:00:18.600 UI development approaches can be categorized as follows: on the Y-axis, we start with zero UI, and on the X-axis, we measure developer happiness.
00:00:24.960 We move from a beautiful reactive UI to low developer happiness, which often involves struggling with JavaScript and CSS.
00:00:33.300 Conversely, high developer happiness comes from the natural beauty of Ruby.
00:00:39.059 Implementing Ruby backend code is definitely an enjoyable experience.
00:00:46.440 However, you don't get any UI output from it. Implementing Rails views like ERB or Haml, often sprinkled with JavaScript, may yield a visually appealing but frequently insufficiently interactive UI.
00:00:57.180 This approach usually requires numerous full-page reloads, and it definitely pushes you out of your Ruby comfort zone.
00:01:10.500 You find yourself jumping between Rails view files, CSS, and various distributed JavaScript snippets, which can be quite frustrating.
00:01:16.979 And then there's the question of adding a second front-end application written in JavaScript.
00:01:23.040 Good luck managing two states: the server-side and client-side state must be synchronized manually via an API, which often leads to headaches from managing complex data flows.
00:01:29.220 Constantly switching between two programming languages and different architectural systems is not ideal.
00:01:41.100 Luckily, last year brought us tools like Hotwire and Stimulus Reflex, which offer some level of reactivity abstraction that helps you avoid excessive JavaScript and allows greater use of Rails.
00:01:54.299 This is great, but I still feel that something is missing when it comes to the developer happiness I experience while writing Ruby backend code.
00:02:07.320 I'm talking about aspects like object-oriented programming, class inheritance, modules, and simply the beautiful syntax that leads to readable and maintainable code.
00:02:20.700 Wouldn't it be wonderful to enjoy all the benefits of Ruby while also crafting beautiful reactive UIs?
00:02:26.700 That's at least what I thought.
00:02:33.060 By the way, if you find yourself down here in zero UI, you're doing it wrong!
00:02:39.420 But maybe it's time to introduce myself. Hi, I'm Jonas. I'm a full-stack software developer working for various clients in Germany, and I'm a huge fan of Rails.
00:02:52.140 I have always worked in contexts where small teams with limited time and budget resources needed to deliver a lot.
00:03:01.319 That's where Ruby and Rails fit in perfectly, empowering small teams to achieve great things.
00:03:19.200 I am truly honored to be talking at RailsConf this year. I would have loved to meet all of you in person, but let's connect on Discord after this talk.
00:03:32.700 I am recording this talk from my small office in Dresden, and that's a picture of me moving in last summer.
00:03:44.459 I am supported by my beloved espresso machine, which makes great cappuccino. And by the way, I've tried my best, but I'm still not a professional artist.
00:03:57.299 Well, maybe I should stick to software. This brings us back to our topic.
00:04:04.680 Back in 2018, while traveling through Australia, I had a small business idea for team management software. Like quite often, this required a reactive UI.
00:04:17.760 I strictly refused to dive into the JavaScript single-page application madness again; I had suffered too much the previous years.
00:04:25.800 I questioned everything I did to create reactive web UIs and started thinking about implementing them in pure Ruby.
00:04:37.919 My goals were simple: create appealing reactive web UIs while enjoying high productivity and, of course, high developer happiness.
00:04:55.680 This, in turn, would result in high implementation and regeneration speed.
00:05:01.259 So I knew I needed to get from zero UI back up to a beautiful reactive experience.
00:05:06.360 After several iterations and unconventional implementations, I ended up with something that genuinely worked and was quite valuable.
00:05:20.460 That is how Mate Stack was born back in Germany. I set aside the idea of developing that team management software.
00:05:34.020 I teamed up with two friends to establish a small tech startup, aiming to build an open-source project on top of Mate Stack.
00:05:47.340 I envisioned that the value of Mate Stack could be monetized, comparable to something like Sidekiq.
00:05:54.539 However, that business model didn't prove successful enough, despite excelling technically in various real-world applications built on Mate Stack.
00:06:06.600 So, business growth wasn't as smooth as I had hoped, and I realized that building a company while managing an ambitious open-source project wasn't the right fit for me.
00:06:13.259 A few months ago, I decided to downsize the company and focus on my mission: growing a community of core contributors to create a sustainable UI toolkit.
00:06:25.259 This toolkit is designed for Rails developers worldwide. But before we talk about open source and the journey ahead,
00:06:31.740 let’s say hello to Mate Stack.
00:06:42.620 Mate Stack is based on a strong foundation of Rails that includes routing, controllers, models, and so on.
00:06:53.280 Our goal is to create beautiful reactive web UIs without getting into the complexities of JavaScript.
00:07:04.259 So what do we need? Well, we need HTML, JavaScript, and styling.
00:07:16.500 Rather than writing traditional HTML, we'll construct HTML structures using pure Ruby.
00:07:29.580 Instead of writing JavaScript, we will compose pre-built reactive components in pure Ruby. And finally, for styling, we will compose pre-built styled components in pure Ruby.
00:07:41.280 Let's begin with the first building block of Mate Stack: HTML structures implemented in pure Ruby.
00:07:46.039 To render HTML in the browser, we get to choose how we create it.
00:07:54.060 I would like to ask you: what do you prefer? ERB, Ruby Haml, Slim, or pure Ruby?
00:08:01.319 Do you prefer a limited templating engine syntax, or would you like the pure Ruby experience that unlocks all the features of this powerful language?
00:08:07.979 Consider the HTML structure of a Bootstrap card component. Now, observe how this structure appears when written in pure Ruby.
00:08:19.919 Mate Stack's basic UI DSL is not abstracting anything; it remains as flexible as pure HTML.
00:08:30.300 We will, however, abstract some elements along the way for simplicity.
00:08:42.600 The same amount of code is required, but we can now craft our UI code with beautiful Ruby syntax rather than HTML syntax, which can become cumbersome.
00:09:07.260 Hence, Mate Stack's rendering converts pure Ruby into HTML.
00:09:19.920 But where do we put this code? We encapsulate this code within a Ruby class, which we call a Mate Stack component.
00:09:26.519 When a new instance of this class is created, the main response method will return the desired HTML string.
00:09:38.760 Let’s start refactoring a bit. We can easily break out of deeply nested HTML structures and create a flatter implementation.
00:10:00.620 This allows us to split our UI code into smaller, manageable chunks.
00:10:08.760 Instead of deeply nested syntax, we can simply call Ruby instance methods, improving the readability and maintainability of our implementation.
00:10:22.560 Can you imagine the power of this when working with complex UIs?
00:10:29.760 Let it sink in, and now let’s make it a bit more usable.
00:10:36.720 Reusable components can accept required or optional parameters to dynamically render injected content.
00:10:49.980 You simply define a straightforward component API at the top and utilize the injected options throughout your component.
00:11:06.000 Components can take blocks or even named slots, making them incredibly flexible.
00:11:11.160 We will see an example of that later on.
00:11:17.279 Now, how do we integrate this Ruby class into a Rails app? There are two integration modes.
00:11:30.180 We can call Mate Stack components in Rails views or utilize Mate Stack components on dedicated Mate Stack pages.
00:11:42.660 This is interesting! We will start with utilizing Mate Stack components within existing Rails views.
00:11:57.480 We can simply call our component class and pass in the required and optional parameters, like title and content from an Active Record instance.
00:12:05.160 Alternatively, we could substitute the complete Rails view with a Mate Stack page.
00:12:17.160 The same principles apply to components; we create a class and a main response method, using any custom instance methods to organize our implementation.
00:12:31.740 Of course, we can call our components here as well.
00:12:36.600 To integrate this Mate Stack page into Rails, we simply instruct Rails controllers not to render the Rails view but rather the Mate Stack page instead.
00:12:43.440 Thus, Mate Stack can progressively replace or coexist with the UI layer of Rails.
00:12:48.600 All other concepts, such as routing and controller-based authentication or authorization, will remain intact.
00:13:09.090 We haven't touched on reactivity yet. Mate Stack can actually be used with various reactivity systems or none at all.
00:13:22.040 If you don't require reactivity, then you're done! However, we want a holistic solution for creating reactive UIs in pure Ruby.
00:13:38.760 That’s why we've decided to ship an optional reactivity system based on Vue.js within Mate Stack.
00:13:44.380 Now let's look at the second building block of Mate Stack: pre-built reactive components composed in pure Ruby.
00:13:56.640 Why would you want to do this? Well, ninety percent of reactive functionality is based on recurring patterns.
00:14:02.940 I created a small JavaScript app to showcase these patterns, so it’s demo time!
00:14:15.060 In this JavaScript app written in Vue.js, I want to demonstrate the recurring patterns involved in implementing reactive UIs.
00:14:30.420 Here, you see a small Twitter clone, where we can tweet and navigate through various pages.
00:14:42.660 On the timeline, there's a form, and submitting this form will give you immediate feedback if any inputs are missing.
00:14:54.540 These error messages come from an Active Record instance that requires specific input, and this is shown without a full-page reload.
00:15:00.700 Let's go ahead and submit the form with the required input.
00:15:10.699 Now, I've posted something, and the form has reset itself, with the UI updating to show our new tweet.
00:15:20.699 Additionally, we can increase the database column count for the tweet, such as a simple likes counter. When I click it, it triggers a server-side action that increments the saved likes counter.
00:15:39.140 But I immediately see the updated number on the UI without needing to refresh the page. This is a typical requirement for a reactive UI.
00:15:54.680 Now let's post something again to have a few tweets.
00:15:55.010 Toggle UI state dynamically based on client-side conditions. For example, if I want to show inline editing, I can click on edit, toggling the UI state to allow me to edit my tweet.
00:16:13.740 After editing my tweet, I can hide the edit form with just a click, demonstrating how the UI state switches without reloading the page.
00:16:35.639 When discussing real-time reactive UIs, it's often about syncing web clients. I want to show you how multiple clients can be synced to see updates from one client on another instantly.
00:16:59.339 For this demo, I have two web clients connected in different browsers. As I post another tweet, both clients are synchronized, and their likes counters can also be synced.
00:17:28.500 An interesting feature is lazy loading, which might help improve the initial page loading speed for this Twitter clone.
00:17:43.180 While the form loads immediately, tweets can be lazily loaded, enhancing the overall speed. To summarize, we have dynamic page transitions, reactive feedback forms, and UI state toggling.
00:18:02.400 We have multiple clients that can sync and perform updates efficiently without reloading the entire page.
00:18:26.700 These are all repeating patterns found in reactive web UIs, which traditionally require extensive JavaScript to implement. Let's see what Mate Stack has to offer.
00:18:36.900 Of course, 90% of the required reactivity centers around recurring patterns.
00:18:41.520 So rather than reinventing the wheel by implementing these generic features with JavaScript every time, why not build and maintain them once and reuse them across all your applications?
00:18:58.260 We already did this! The demo I just showed you was built using these pre-built UI components without writing any custom JavaScript for the reactivity you saw.
00:19:13.260 I utilized the transition component for dynamic page transitions, the form component for reactive forms, and the async component for partial UI updates.
00:19:21.740 By using the click component, I could toggle UI states based on user interactions.
00:19:31.860 These are just a few of the examples showing how you can compose a reactive UI without having to write JavaScript, using only pure Ruby methods.
00:19:48.460 How does this work? Let’s examine the functionality of the onClick and toggle components. Imagine you want to toggle the view state somewhere else in the UI.
00:19:59.040 With Mate Stack, you call Ruby methods like onClick and toggle while passing in required options and a block that targets the specific component.
00:20:14.440 This event triggers the rendering of a special HTML component tag with configuration hashes injected as attributes.
00:20:32.580 When the browser receives this, the Vue.js components noted in Mate Stack's JavaScript are mounted and the configuration hash is injected.
00:20:40.160 At this point, an event hub is established to facilitate communication between all components. We define a button that presents 'Click Me'.
00:20:57.540 When the button is clicked, the 'hello' event is emitted and subsequently received by the toggle component, revealing a span with the text 'Hello World.'
00:21:13.680 This illustrates how the inline editing capability was implemented, allowing dynamic toggling based on specific configurations in our components.
00:21:26.520 The action component is designed to call Rails controller actions from the browser. Think of the like button in the demo.
00:21:35.280 Within a Ruby response of a page or component, you can call the action component and configure it with a hash that specifies the Rails controller action path and desired HTTP method.
00:21:41.880 Being able to show a button configured to trigger a specific server-side action adds functionality to your pages.
00:21:53.760 Now let's discuss the form component. In pure Ruby, we call the Mate Stack form component and pass in a configuration hash.
00:22:08.160 This time, we use a helper method to keep implementation clean. We inform the form component to collect user inputs and perform a background HTTP request upon submission.
00:22:15.960 As it interacts with the Rails controller, the controller reacts with positive or negative status codes.
00:22:21.780 Negative codes usually result in server validation messages, such as those resulting from Active Record validation.
00:22:35.420 These errors are received and rendered next to the relevant form input fields in the browser.
00:22:52.080 If the Rails controller action responds with a positive status code, the form component emits a 'submitted' event.
00:23:07.740 This event can then be used by other Vue.js components in the browser.
00:23:18.420 Let’s discuss the async component. It is designed to handle partial UI updates by fetching fresh content from the server.
00:23:30.720 In the Twitter demo, as you recall, after submitting a form, the list of tweets updated without reloading the entire browser.
00:23:38.580 This is achieved by configuring the async component to re-render itself based on a specific event, such as a successful form submission.
00:23:51.059 The async component performs a background HTTP request to the Rails controller, which then re-renders the UI on the server side.
00:24:04.140 Only the necessary HTML content is sent back to the async component in the browser where it performs DOM updates.
00:24:14.640 What about the synchronous multiple clients? The good news is, async doesn’t care where the requests originate.
00:24:30.420 Thanks to the simple Action Cable integration, events can be broadcasted from the server to all connected web clients.
00:24:41.880 These events flow through Mate Stack’s event hub, allowing connected clients to refresh and render as needed.
00:24:49.740 You might question why we need to re-render the entire UI on the server for a simple update.
00:25:00.880 Great point! However, for optimization of partial UI updates, you can utilize Mate Stack's cable component.
00:25:10.680 In this instance, the cable component can perform targeted actions based on specific events, yielding an optimized structure.
00:25:27.240 The cable component allows more fine-tuned control compared to the async component, although it calls for additional implementation effort.
00:25:44.140 Conversely, for simple use cases, the async component performs efficiently.
00:25:54.360 Now let's talk about the transition component, which facilitates dynamic transitions from one page to another without a full reload.
00:26:09.240 For this to work, you need at least two Rails routes that point to two Rails controller actions.
00:26:25.200 The role of Mate Stack app is to wrap all page responses and act somewhat like a Rails layout.
00:26:38.760 The app can yield pages as needed and define transitions between actions.
00:26:55.020 On the initial request, the requested page and its app will be rendered together and shipped to the client's browser.
00:27:10.800 The system automatically mounts the page content view component, allowing for smooth transitions.
00:27:24.240 When the transition button is clicked, a background HTTP GET request directs to the specified Rails controller action.
00:27:41.220 The action responds with the HTML string generated by the designated Mate Stack page. This HTML is sent back to the browser.
00:27:57.900 The page content view component then updates the displayed page with new content. The rest of the layers remain unaffected.
00:28:13.480 Transitions can also emit events, notifying other components to react to page transition events.
00:28:28.080 As we’ve seen, we have generic pre-built reactivity that’s sufficient to cover 80-90% of the standard requirements for developing a web UI.
00:28:41.535 Mate Stack encourages a mindset shift: instead of implementing reactive features and managing front-end data flows yourself with JavaScript, use Mate Stack's growing library of components.
00:28:56.160 The heavy lifting of implementing JavaScript is already taken care of.
00:29:05.640 If you need custom reactivity that can't be achieved with built-in components, you can easily add your Vue.js components alongside them.
00:29:20.760 This is how a custom Vue.js component referencing its name appears in Ruby.
00:29:38.460 Then create the corresponding pure Vue.js component, where you can leverage everything Vue.js offers.
00:29:55.200 In addition, you can use the event hub API to communicate with all other Vue.js components, whether they are built-in or custom.
00:30:08.280 For instance, in this case, our custom component interacts with a toggle component, responding to a received event to display its content.
00:30:23.220 Using this approach, it’s possible to enhance the last 10% of the custom reactivity your app may require.
00:30:42.960 But often, the pre-built reactive components are sufficient on their own.
00:30:58.020 Just think about how much JavaScript hassle can be avoided by utilizing Mate Stack's pre-built reactivity system while retaining flexibility.
00:31:04.740 This flexible abstraction allows you to focus on building rather than struggling with frontend complexities.
00:31:13.500 Now let's discuss the final component of Mate Stack: pre-built styled components composed in pure Ruby.
00:31:30.780 Why would you want to use these pre-built styled components? Consider how tedious it can be to copy and paste complex DOM structures and lengthy CSS classes across your application.
00:31:47.760 Recall the Bootstrap card component example from earlier; wouldn’t it be great to have access to all Bootstrap components in pure Ruby?
00:32:01.240 Well, lucky for you, we've already done this! For instance, creating a Bootstrap button only requires a simple Ruby method.
00:32:11.520 You can easily pass optional parameters to customize its appearance. This approach saves you from building out numerous HTML structures and cascading CSS classes.
00:32:24.220 A single line of Ruby can replace six lines of HTML containing various CSS classes, significantly reducing code maintenance.
00:32:38.340 Keep in mind that these components are not merely used once; they are often reused throughout your UI.
00:32:48.640 Our focus is not about saving a handful of lines of HTML here and there; these components dramatically influence a growing UI.
00:33:01.920 What if you need to pass something more complex than a simple string? As I mentioned, components can also accept blocks.
00:33:15.600 Instead of merely passing a string to be rendered in a card body, you can submit a custom block that contains further styled elements.
00:33:26.660 This block will take the place of the default paragraph while maintaining the card's overall structure.
00:33:39.240 Want to pass multiple custom DOM parts instead of just one block? Use slots to define multiple references to your custom methods within pre-built components.
00:33:55.280 These pre-built components will render your custom methods in specifically designated placeholders within the layout.
00:34:11.220 This is especially advantageous when employing more complex components, leading us to the smart collection.
00:34:27.960 Imagine a styled component that beautifully renders a collection of database items with customizable filters and pagination, allowing smooth browsing.
00:34:43.920 It even enables reactive updates and actions without writing a single line of HTML, JavaScript, or CSS—just a few lines of pure Ruby.
00:34:56.500 Sound too good to be true? It really works! Simply call a pre-built component to define the base query, pagination, and accompanying actions.
00:35:05.640 Take a look at this demo before we see the smart collection in action. This app utilizes Bootstrap's default theme; of course, you can create your custom themes.
00:35:24.420 This dummy app makes use of predefined, responsive sidebar layouts and page components to quickly create a clean and visually pleasing UI.
00:35:39.000 It showcases various styled form components for reactive input and employs built-in toasts for user notifications.
00:35:51.180 To further enhance user experience, we utilize the smart collection component to build a reactive and paginated table.
00:36:04.200 This allows us to browse through a dataset and search for individuals based on their attributes.
00:36:19.320 Thanks to custom row actions, we set up a delete function that confirms the action and refreshes the dataset.
00:36:31.740 And if you prefer a non-table format, you can adapt your own list rendering using a designated slot.
00:36:44.640 While reactive pagination and filtering remain intact, you can shape your output in a format that suits your needs.
00:36:59.860 All Bootstrap version 5 components are now accessible as Mate Stack components written in pure Ruby.
00:37:07.720 If you're eager to see how it’s done, check out dummy.maidstack.io to explore this dummy app.
00:37:14.540 Finally, you can experience the beauty of implemented code in pure Ruby.
00:37:24.960 In conclusion, we've managed to bypass HTML writing and instead enjoy implementing structures using pure Ruby.
00:37:36.980 We’ve eliminated the hassle of JavaScript, enjoying pure Ruby for reactivity instead, making it easily extensible with Vue if necessary.
00:37:49.620 We've also circumvented large DOM structures and cascading CSS classes, opting for styled components in pure Ruby.
00:38:03.060 The core features are available in the open-source gem, Mate Stack UI core, while the styled components are offered in the Mate Stack UI Bootstrap gem.
00:38:18.720 Both gems are available on GitHub. And if Bootstrap isn’t your style, consider developing a gem for your custom design system that can be reused across your applications.
00:38:29.640 In any case, I hope Mate Stack will help you escape the front-end hustle.
00:38:41.760 We've discussed beautiful reactive web UIs implemented in pure Ruby, but now it’s your turn.
00:38:49.140 Go to GitHub and give the Mate Stack UI core as well as Mate Stack UI Bootstrap a star if you appreciate this project!
00:39:01.440 Follow Mate Stack on Twitter for updates and useful tips, and dive into docs.maidstack.io to get started.
00:39:12.420 You can find a detailed guide for building the Twitter clone step by step.
00:39:25.500 Feel free to join our Discord server, say hello, share feedback, and request community support.
00:39:36.480 Create issues, report bugs, or propose features and get personal onboarding from me to become part of the core team.
00:39:55.700 Help us in building an amazing tool for Rails developers and enable them to find the joy in developing reactive web UIs again.
00:40:05.579 I look forward to getting to know you better and am happy to answer any questions about Mate Stack on Discord.
00:40:11.599 Join me for the RailsConf Q&A session starting on April 14th at 2:30 PM Eastern Time.
00:40:18.540 Feel free to reach out anytime online. Thank you for listening, and have a wonderful Rails Conf experience!