Rocky Mountain Ruby 2023

Turbo Frames Explored... for Fun and Profit

Turbo Frames Explored... for Fun and Profit

by Jon Sullivan

The video titled "Turbo Frames Explored... for Fun and Profit" features Jon Sullivan at the Rocky Mountain Ruby 2023 event. The focus of the talk is on Turbo Frames, a part of the Hotwire framework that enhances Ruby on Rails applications by reducing the need for extensive JavaScript.

Jon begins by introducing his background and his enthusiasm for Turbo, explaining that the Hotwire framework, released at the end of 2020, consists of various parts including Turbo, Stimulus, and Strada—each serving distinct functions. He emphasizes that Turbo Frames are designed to handle sub-resource interactions efficiently, streamlining web development tasks.

Key points discussed include:

  • Turbo Overview: Turbo Frames are positioned as a layer that simplifies page interactions through efficient navigation and data handling without full-page reloads.
  • Ease of Implementation: Jon illustrates that using Turbo Frames typically requires much less code than writing equivalent JavaScript solutions, often needing only a few lines of Ruby.
  • Examples of Use Cases:
    • Pagination: Turbo Frames can handle pagination effortlessly with minimal code.
    • Lazy Loading: He describes two types: eager and lazy lazy loading, demonstrating how to implement these features without complex JavaScript.
    • Job Feedback: A pattern for providing user feedback during long-running jobs without blocking the main thread is introduced.
    • Modals: Jon showcases how Turbo Frames can be used to manage modals with little overhead, enhancing user experience.
    • Wizards for Multi-Step Forms: The method is shown to keep the user journey fluid and seamless, making it easier to manage state through different steps of a task.

Jon concludes with a practical framework for understanding Hotwire's components, reiterating that Turbo Frames can greatly reduce both the code and complexity often associated with JavaScript-based approaches. He advocates for the adoption of Turbo Frames, citing their efficiency and ease.

Overall, the session emphasizes how Turbo Frames can facilitate rapid development in Ruby on Rails with less reliance on extensive JavaScript, improving both the developer experience and the end-user experience.

00:00:15.240 All right, a show of hands. I was given the opportunity to potentially make this slot a nap. Is everyone in favor? Spike, uh, can't do it? Okay, got to give the talk. I'm sorry, I tried.
00:00:21.600 Hi, I'm Jon Sullivan, or sometimes Jon Sully, because my internet pseudonym creativity is really good. I've been doing Ruby on Rails for going on seven years now, and I am a Turbo Enthusiast.
00:00:33.800 Before we jump in, I also want to give a big thanks to the whole team. If you could give a round of applause that would be awesome. Thank you guys for putting this on and all the volunteers; it's been really great.
00:00:40.719 I have a background in audio and lights, so thank you to you guys back there. You are great; the sound system is fantastic. Well, there have been some great talks today, right? Really good content! I'm really happy about it.
00:00:52.920 Today, I want to talk to you about Turbo Frames explored for fun and profit. I really want to go back to the start. I'm going to show something from back when it was called Twitter; new magic was released at the end of 2020. I think a lot of us probably remember this big fanfare, but whether or not it's been picked up by everyone has been kind of hit and miss, just because it's a totally new framework and system.
00:01:24.400 Let me do an interactive exercise here. Could you raise your hand if you like writing JavaScript? It's okay, it's all right; no one's going to bite! I like writing JavaScript. Now, could you raise your hand if you like writing Ruby? Yeah, it's a conference; that's cool. Keep it up if you prefer writing JavaScript to Ruby. And it's also okay! No, it's all right; I appreciate your honesty. Now just keep it up if you also love writing Webpack configs. I know, I know.
00:02:19.120 This was the idea that I feel was presented so much with the new magic. A lot of the Turbo stuff is sort of 'down with JavaScript; we have a new way platform.' I think that adversarial approach is maybe the reason why it hasn't seen the full uptake that everyone wants.
00:02:30.920 I'm here to give you a different message: it's not against JavaScript. I like JavaScript; it's fun! But that portrayal doesn't capture why Hotwire is actually so great, and the reason is that you just write way less code. There's nothing wrong with JavaScript, but two lines of Ruby are way easier to manage than 300 lines of JavaScript.
00:02:42.280 So, I want to go back to where we started. When Hotwire was released, we got several new pieces: you have your Rails backend, and then we have this new Hotwire thing, which itself is made up of three big pieces: Turbo, Stimulus, and Strada. When we dive in, we have Stimulus, which got rewritten because the Basecamp developers wanted us all to have to rewrite our controllers. Thanks, guys! We have Strada, which just came out a couple of weeks ago. I haven't touched it at all, but it seems really cool for native app stuff, iOS, and Android.
00:03:05.480 Then we have Turbo, which itself is also three pieces: Turbo Drive, Turbo Frames, and Turbo Streams. Today we're going to be talking about Turbo Frames specifically. I want to go into the three pieces of Turbo Frames that I think are really cool, and I want to talk about them. Turbo Frames can do cool things, but more importantly, they can do cool things built faster with less code and less thought on front-end development.
00:03:31.400 I'm not going to get into every single implementation detail here; that's not the point of this talk. But I want to show you some neat stuff that is pretty easy to implement if you want to dig into it. This is all stuff we're running in production at my company, Agent Pronto. I've worked there with my team; we’re all here; it’s a real small group. But we've been running it for a couple of years, and it's been really awesome for us, we've been able to maintain it with almost no thought, which is really exciting.
00:03:55.120 Let me go back a little bit. We're talking about Turbo Frames, which is sort of the middle layer. It’s a bit hard to reason about Turbo Frames unless you understand what Turbo Drive does. For those of us that have been in the community for a while, we had TurboLinks, which got deprecated for Turbo; new names are cool! But now it's Turbo Drive. Turbo Drive is essentially the default glue that you just turn on, and it allows you to change pages seamlessly without reloading the entire browser.
00:04:14.120 When I think about that resourcefully, I like to think about it as changing which resource I’m looking at. So if we go to the classic library books and authors demo, we go from one book to another. We're looking at one book, and then we switch to another one. Turbo Drive handles that really seamlessly. I'm going to give you an example of that in practice. Oh, this is a live coding talk! Thank you! Like I said, I work for Agent Pronto. What we're going to be looking at is Agent Pronto's web interface, mostly running locally because that seems safer. We use Turbo Drive on our public website.
00:04:58.759 I'm going to switch between two different pages that kind of explain what Agent Pronto is. So, what is Agent Pronto? Agent Pronto is a service where if you're looking for a realtor, we help you find a good realtor; it's pretty simple. But like everyone, we have a very nice website to help explain that to you. We have two pages here: 'How It Works' and 'About Us'. As you'll notice as I click through them, the browser isn't doing full-page loads. In fact, Safari would show a red loading bar here; it would have to repaint everything. That's not happening. Instead, we're seeing this really fast single-page app-like transition between these pages.
00:05:52.240 We can see that on the server side, it's still loading these full pages from the server. As far as Rails knows, that's a totally normal browser client loading the full page. Turbo Drive just gives us that seamless swap between resources with pretty much nothing required. In fact, so little is required that the code is very literally just importing Hotwired Turbo Rails.
00:06:09.479 Matthew mentioned this earlier, but it is one of those systems where the default is to take over the entire browser state and just do the thing automatically. And for a lot of reasons, that's convenient, not always, but usually.
00:06:20.080 Let’s take an example layout. It’s got a couple of tags, some boilerplate, and inside that JavaScript pack tag, all we have is that import, so there’s no hidden wires or anything. It is that easy! We have a couple of Stimulus things there below, but it’s pretty straightforward.
00:06:37.680 Now, I want to go up a layer and talk about Turbo Frames finally. If Turbo Drive is switching between resources, I like to think of Turbo Frames more as interacting with a sub-resource or paginating. For instance, in the case of books, maybe you’re looking at a book page, but you have several authors or you're switching between authors on that book page—that's a really good case to use a Turbo Frame.
00:07:01.919 Pagination is also a useful case for that. This would be our testimonials page. It's a little different in production, but I've shortened it up for this conference; it's pretty basic: 'Hey our site is great, use us!' and here we have a list of different testimonials. The nice part is that as I click through these, they just pop right in; the browser isn’t changing at all. I'm not reloading whole pages; in fact, my URL and the address bar isn’t changing either.
00:07:35.360 Again, we can still see that the browser is serving these things, but I'm not getting a full-page experience—I'm getting a single-page app experience. Well, that's really cool! How do we get there? How about two lines of Ruby? Now you have full pagination on your testimonials, and all it is is a Turbo Frame tag start and end. Maybe the second one isn't even fully counting, but it's two lines, and that's really accessible.
00:08:09.120 I don't have to write any JavaScript for pagination stuff. Again, I'm not saying that writing that stuff is bad; I'm just saying this is easier, and I'm lazy, so it's great! I want to explain how this works a little bit and I'm going to start at a high level.
00:08:23.680 I'll walk through the basics of how Turbo Frames work, and then we'll build up from there. We'll see some cool examples. As I said, this is a diagramming tool—it's great for diagrams. If you'll indulge me a little bit, we're going to walk through an HTTP response in Rails. Assuming a request already came in, this is kind of how the response is built up. Usually, it starts with the controller, goes through a model, then we render a view, and it goes to the client.
00:09:11.560 This view has a little Turbo Frame tag in it—there's nothing really special here; it’s just an HTML tag. Ultimately, it renders in the client's browser and it has the frame in the DOM at that point. So what's going on is when you click something inside that frame, a link (Turbo) on the front end still, but in the background, Turbo goes and requests that page from the backend server.
00:09:56.720 The backend server treats this just like any other request. It renders some view, sends it over the wire; it’s just happening in Turbo's internal front-end code and not in the browser—Turbo's entire job is to find the matching frame in the new response and replace that frame in the original DOM with the new code. It doesn’t touch anything outside of that; everything else stays the same. That's its entire job.
00:10:56.880 The frame gets updated with the new content, but the address bar didn't change at all; it's the same page, you're still looking at the same resource, and just something within that page is changing. The other nice part is that the backend didn’t have to do anything special; it didn’t even know it was handling Turbo. It just rendered two different paths with different requests. This is very simple; we didn’t have to change anything.
00:11:43.640 Now, Turbo throws away the rest of the markup, so we get a full-page response, but it throws away everything else because the only thing it injected was the frame. While that feels a little wasteful, the nice part is that when you run Turbo Rails, it skips rendering some of the things in that response because it knows it's coming from a frame. So you're still wasting a little bit, but not too much.
00:12:54.760 So, Turbo Frames, when navigating from within a frame, replace the content of this frame with the content of the same-named frame in the response. That is how Turbo Frames work—pretty much it.
00:13:12.720 I want to get into some of the meat of this talk and discuss the cool things that Turbo Frames can do. This is the foundational understanding of how Turbo Frames work. Take some stuff, put it in where it was, and ignore the rest of the request. But we can get pretty clever with Turbo Frames, and I want to walk through a few examples.
00:13:47.759 The first one is Turbo Frames for lazy loading. This is one of the cool things you can do with less code. It's unfortunately a little more complex than that; Turbo Frames call lazy loading either eager lazy loading or lazy lazy loading—very clear!
00:14:12.960 The difference is careful: eager lazy loading means that you have a frame on your page that loads initially with stock unloaded content, and as soon as the page is rendered, it pulls that content immediately once the page finishes loading.
00:14:45.160 Lazy lazy loading is when the frame is loaded after the page is loaded and not until you scroll it into the viewport. This loosely mimics what's available in iframes and images right now using the native browser lazy options, and the goal is similar but distinct.
00:15:23.440 Let's talk about eager lazy loading first. The good news is that adding it to a frame is really easy. You add a source attribute to the frame, and whatever is inside the frame initially is what you see first.
00:15:55.520 Then the source is what gets loaded; it pulls that path as soon as the page loads. It's another thing that's easier to see in practice with cats. So this is our homepage—not as it is in production—where we have a little demo.
00:16:18.440 When you load the page, you can see it finishes loading, and then it goes and requests that frame. The default content is 'Please wait for cat,' and once the cat loads, it slots into that spot.
00:16:50.440 This is pretty canonical lazy loading. All we had to do to achieve that lazy loading is wrap our content in a Turbo Frame. We didn’t have to do anything special on the front end.
00:17:18.319 Now, if we want to talk about lazy lazy loading, this is a little different. The frame is lazily loaded, but it's going to wait all the way until you scroll that content into the viewport of the browser to load.
00:17:56.880 Also quite easy to add, just the same thing: you need a source attribute that tells Turbo where to load that frame from, plus you just add loading='lazy' to it. That will tell it to wait until it's in the viewport to load the content. Let's look at more cats; that seems to have worked!
00:18:37.760 Up here at the top of the page I have this eager lazy loading thing, but lower down on the page, I have a second cat, and it's not until I scroll it into the viewport that it starts to load. You can see that in the server log; another request will pop up as soon as it’s there.
00:19:06.680 Most importantly, when I first load the page, that request doesn’t pop up because it’s not loading until it’s in the viewport. So, we have two different options for lazy loading with Turbo Frames, and they’re pretty easy to install; it’s just a couple of attributes on a Turbo Frame.
00:19:31.680 But it actually is more powerful than that; see, these are images and you can lazy load images right now natively in a browser. What’s really cool about Turbo Frames is that anything you wrap in a Turbo Frame can also be lazy loaded.
00:20:20.640 In this example, same homepage, it was easy because there was already a frame there, but here we have some other content that takes a while to load—in this case, it's 300 digits of pi, and it takes a while to compute that.
00:20:59.840 Now we have lazy loading that we can use for images and other content, like charts that require a lot of horsepower to render, or database queries that may need optimization.
00:21:34.800 The second thing I want to talk about is Turbo Frames for job pingers. This isn't an official name, but it's a pretty common pattern where we have a user who submits something to trigger a job, and we want to show them feedback without holding up the web server.
00:22:05.840 We want to show them a loading bar, and while the bar is loading, it'll ping the backend: 'Hey, is the job done?' This usually takes some JavaScript to keep pinging the backend.
00:22:25.640 This is a terrible idea I had that is still in R&D. It’s like lazy loading but on repeat. You have a source that causes it to lazy load, and if the source is the same path you just loaded, it loads again.
00:23:06.440 This means it keeps querying the server until the job is finished. I don’t have a ready demo for you, but that’s the concept—lazy loading on repeat.
00:23:46.080 Next up, we have Turbo Frames for modals, another feature that typically requires complex JavaScript, but we’ve enjoyed using it in production because it simplifies our implementation. I have a demo here about Agent Pronto.
00:24:09.680 When a user wants a realtor, they’re matched with agents, and we show them a dashboard of all the realtors available. Clicking 'View Profile' pops up the modal without any JavaScript; it's just the right HTML.
00:24:43.440 We handle modals easily; when the profile is requested, we still use Turbo Frames to load that data and present it as a modal, just like Turbo Frames works day-to-day.
00:25:13.920 We do this by having an empty hidden Turbo Frame in the layout at the bottom of the markup—this acts as our modal target. The power of an empty Turbo Frame allows us to control what's displayed without additional JavaScript.
00:25:47.600 When the link to view a profile is clicked, it behaves as if it's part of the modal frame, triggering a new request that adds the content to that frame. When we want to close the modal, we simply send an empty response—leaving the frame present but empty—so all we see is the original content.
00:26:24.400 This gives us an efficient way to manage modal interactions with minimal backend code! Turbo continues to prove how powerful empty Turbo Frames can be, making use of space when necessary.
00:27:01.760 Now the fourth example I have for you is what I like to call 'wizards,' which are essentially multi-step forms that guide a user through several smaller tasks rather than one large task.
00:27:42.960 We have this wizard where you're asked details like where you're buying, what type of house, etc. Now what’s useful here is that with Turbo Frames, the content is dynamically loaded onto the existing page. As you fill out each step of the form, Turbo replaces just that section, giving you great UX without full reloads.
00:28:27.920 Even cooler is that the layout in which you go through the wizard can differ from the standard application layout—allowing for a more optimized experience based on the stage of the user journey.
00:29:07.240 By utilizing Turbo Frames effectively, your navigation and interactions remain seamless while also allowing the browser history to keep track of your steps.
00:30:00.360 I wrote a long guide on this topic if you're interested, it has a full breakdown and various examples. You could find the guide quite useful!
00:30:54.000 To sum up, Turbo Frames can do amazing things quickly, typically with much less code compared to JavaScript, and generally require less thought about front-end concerns.
00:31:21.000 Turbo Frames can handle pagination and sub-resource interactions, allow for lazy loading in both eager and lazy ways, can ping job statuses and callbacks seamlessly, handle modals without needing extensive code, and support multi-step forms.
00:32:10.440 Finally, I want to give you a framework for considering Hotwire as it contains various parts, each serving their purpose. Turbo Drive handles navigation seamlessly, keeping things straightforward.
00:32:30.000 Turbo Frames are more about working within UIs, managing sub-resources, and facilitating asynchronous operations effectively. You also have Turbo Streams, which handle real-time updates for content changes, and Stimulus, which provides interaction at a light level.
00:33:30.680 I wanted to thank my employer, Agent Pronto! My team is here; feel free to say hi. We run Hotwire Turbo and Rails 7 in production, and it's a great experience.
00:34:11.680 This has been Turbo Frames explored for fun and profit! My name is Jon Sullivan, or Sully, depending on my creativity for the day. If you have any questions, feel free to email me. Thanks for listening!
00:34:38.440 I would love to take questions if anyone has some. Yes, up front: No, I haven't. The question was whether I made an infinite scroll with the lazy-loaded Turbo Frames.
00:35:10.360 That's an interesting idea! I like that; I haven’t attempted it yet. Can Turbo Frame added content contain new Turbo Frames? Yes, I believe you can do that.
00:35:46.440 You can have recursively Turbo Frame yourself to Oblivion with thousands of new frames calling thousands of new frames. But it becomes a matter of ensuring Turbo is aware of the new frame that has been loaded and handling it appropriately.
00:36:15.360 In terms of Rails upgrades, the stack has stabilized significantly. It was pretty unstable, especially in the beginning, but it has made good advancements since then.
00:36:59.160 The Turbo Framework is designed to handle upgrades and maintain compatibility with existing Rails applications, so it has every chance of becoming an essential part of modern Ruby on Rails development.
00:37:33.840 Turbo Frames allow much easier state management without excessive JavaScript needs. You don't have Turbo saving state; it’s about how you choose to manage that state.
00:38:10.680 Thank you for your time! Feel free to reach out if you'd like more insights!