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!