Migrating 50,000 lines of code to React Server Components with Darius Cepulis === [00:00:00] Hi there, and welcome to PodRocket, a web development podcast brought to you by LogRocket. LogRocket helps software teams improve user experience with session replay, error tracking, and product analytics. Try it for free at logrocket. com today. My name is Paul, and joined with us is Darius Jaboulis. He is the Senior Dev Community Manager over at Mux. And we're going to be going over his recent talk he gave at NextConf 2023 about how they migrated 50, 000 lines of code to React server components. And he lived to tell the tale. Might sound a little scary, but we're going to dig into why it might not be so scary, and, how you can even approach that idea. So welcome to the podcast, Darius. Excited to dig in with you. Cool. Thank you so much. Great to be here. So before we dig in... Two specifics and your migration, we talk a lot about RSCs on this podcast and in your talk, you say, this talk I'm giving right now. It's not necessarily explaining RSCs. [00:01:00] That's not what this is about. It's about the migration. But for folks who are still getting their hands in that space, can you summarize what an RSC is and how it like manifests in Next. particularly because that's what the migration story is about. React server components are, I could say it's in the name, it's React that can run on the server, but that simple thing really unlocks a lot of things. The fact that you can choose where your code runs. If my react runs on the server, it runs on the client means that you can start making interesting decisions. If it runs on the server, then maybe you can start doing server things, accessing your database or like running huge libraries, stuff like that. And of course, react is always run on the client. So react that runs on the client, you get the fun things like user interaction state. Effects. Oh, I love effects. Don't we all, especially when you don't know why it's happening. It's a you get to choose where your code runs. And what about people who don't normally make that choice? They just put everything on the client, right? \ ? So one thing you mentioned is a big library, right? [00:02:00] That's a good candidate of something. You might want to choose. What are some other things that you had your attention turned towards \ immediately? I know this is going to be good as a server component and doesn't have to relate to your migration, just like in your dev life, we could talk about what makes a good candidate for server components and client components, and I think that's an important conversation to have. \ But one thing that like definitely clouds the room with server components is just all the complexity, all the historical baggage. And every time that comes in, it just instantly gets overwhelming. You asked what makes a good candidate for a client component and what makes a good candidate for a server component. Huge libraries, that's obvious., we talked about it earlier, database access, that's something you've never been able to do in React before. Historically, In Gatsby or Next. js there used to be like special functions like get server side props where you could access your database and now you can just do it straight in a component and that makes such a big difference in so many little ways instead of fetching your data once at the top of a page which is what you had to do in Next. js you can now fetch data directly [00:03:00] in a component so really server components you'll be thinking about reducing bundle size you'll be thinking about data fetching. So what led you and the team to say, now's the right time for us to take the sleep over in Mux? And what does mux do? Just really quick. Mux is video APIs. Anybody who's tried to add video to their app really real quickly realizes that it's a bad idea that there's so many complexities. You could ship a straight MP4 to the browser, but MP4 start getting big. So then you start talking about renditions and stuff like 720p, 1080p. And then you talk about like hosting those renditions on CDNs and everything. And you can make YouTube do this for you, but then you're A set of APIs that let you just upload your video and then just stick them in whatever application you're building for on demand real time. right on. So very data heavy in the back end, cause you have a whole platform behind the scenes. But of course you have an app just every other company out there that's on the web. Yeah, what led you to deciding now is a good time to look at the RFC [00:04:00] paradigm? Yeah. And in fact, I'm glad you mentioned the app. Our MUX dashboard is actually still traditional server side rendered, but our marketing site and our documentation site have moved to react server components in the last year. It was the right time because both of those sites were undergoing deep foundational changes anyway. We were already under the hood. The documentation site needed a new information hierarchy and a new sidebar. And the marketing website we rebranded the whole darn thing in April, which meant that every single component was being touched. Because we were under the hood, and because Vercel and Next. js had just come out with Next. 13, which Ships server components. We realized we had an opportunity. Do we want to move to server components? Now? It's a scope creep kind of making the project bigger. But in exchange for that, making the project bigger, we were able to unlock those benefits of server components and hopefully set up our app to Be going where the future of react is probably going as well. Did you guys feel anxious about scope creeping in that direction? Okay. yes, we did. And it was,[00:05:00] it was actually a little bit of a mistake. I want to say, I want to own that mistake too, because it was my mistake. I'm the one who pushed for this with our documentation site server components, I think. In many ways are simple, but in many ways have some kind of unforeseen complexities which we can get into. I mean, there's, to start, there's already the mental model that we're talking about here. What runs on the server, what runs on the client, that's complexity that you didn't have to worry before And then. Server components are also coming with the app directory which comes with its own changes in Next. js and then there's some kind of deeper changes that we had to make throughout our application because server components have some gotchas that you might not expect. CSS and JS doesn't run anymore. We use styled components and styled JSX. Those don't work anymore. So we had to move to tailwind, which ended up being a good thing. React context doesn't work in server components. So we had to kind of rearrange some of our context, some of our providers and stuff. So am I worried about scope creep? No, but I should have been. That being said, we did it in documentation, it ended up taking probably a month [00:06:00] longer than we expected. But they're all changes that we worked to. Honestly, probably should have made anyway, moving away from CSS and JS, rethinking where we're using context and everything. And here's the real kicker, because we did it once, when it came time to do it again, for the marketing site, we decided yes, it's the right move. The benefits are worth the overhead in our case. When you go about through that transformation there's componentized logic that you're getting out of here. You're like, okay, I can take logic, put in a component that can access my database. And the cool thing about server components is you can pass them as props between client components. You could have a server component in a client component and please correct me if I'm wrong in any of this here, but you could have a server component in a client component passed as a prop. And so that kind of urged this mental model of saying, okay, it could be further down in the tree, but like \ the client boundaries further up. Did you guys like take a waterfall approach? That's what I want to call it of saying, Hey, here's the landscape. Here's. [00:07:00] Where those like lightning bolts of server components are going to dig down into our tree? Or did you just start with one and then march along? How much preemptive planning did you put into that mental model? Yeah, the approach that we took was change as little as possible. Server components have this huge potential to just like completely detrimentally. Leave whole parts of your app on the server, which of course reduces bundle size a lot, but because at least at first we didn't want to rewrite everything, we tried to stick as close to how Next. js did fetching originally, we had One server component at the top. So Next. js by default makes your entry point, your page, a server component. So we had one server component at the top, which did data fetching. And then we left the rest of our application as a client component which works exactly how Next. js kind of used to work, we have one bit of data fetching at the top, and then you pass that data throughout the whole client application. After we did that, and felt okay with that, we [00:08:00] started testing. Moving the client server component boundary lower in our tree, we said, okay, so our root page is a server component. What if the first two components that it imported were server components? So we changed them to server components, checked around for errors. Everything was okay. And we kept moving that boundary lower and lower until We ran into enough errors that we're like, okay, this is good enough. And then the top half of our component tree was a server component. And then the leaves, which required interactivity stayed as client component. What areas of functionality did you find were easiest, to convert into the server component paradigm? I'm glad you asked so much more than you might expect. Our documentation site, I'd say a vast majority of server components right now. Our documentation site and our marketing site though are content heavy sites, right? So all of our content can now stay on the server. We have all sorts of typography components to make sure that our H1s look like H1s and stuff. We've got some components like callout boxes or , block quotes and stuff like that. All of that can stay on the [00:09:00] server. When we started pushing that client. server boundary downward, it ended up at the very bottom of the tree. Then of course there were client interactivity that had to stay on the client. We're a video company, we have mux player, that player still has to be shipped to the client because it's a player, requires clicking and interactivity, stuff like that. So you got things pretty far down in the tree, you're saying. Down to the markdown rendering that's what I'll call it, H one, H two. I know it's not that. But how did you utilize suspense to keep things like your sidebar lively? Because I know the suspense piece comes into play a little more heavily if you're trying to put everything on the server and you have content loading. So yeah, please speak a little bit about that. I'm very curious how you glue that experience together. If you've been reading anything about server components, you really quickly start seeing terms like streaming and suspense tossed around. And that's the. Whole second layer of the server component story that's like really big, really fundamental changes to how you can run your react app, [00:10:00] but it also introduces a lot of complexity, which is why I haven't talked about it much yet. So far, we've only been talking about, you know, we leave some stuff on the client, some stuff on the server. With suspense, you can fetch data on the server and stream it in when it's ready. Our sidebar, thank Requires some data from our CMS is sanity, it runs really nicely, but because it's the only thing that needs data from the CMS, sometimes our sidebar had to wait for sanity, had to wait for that data fetch while the rest of the app just didn't show up and that specific part of the sidebar is our changelog is hosted in our CMS, but if a user is just visiting a different part of the site, if they're just visiting our API reference, if they're just visiting our guides, they don't need that data immediately. , that was a perfect opportunity to use suspense to stream data in. Let's say I'm going to docs. mux. com slash guides. I go to the guide, and as soon as I make that request, our server says, hey, sanity, what's new in the CMS? But instead of waiting for that response Next. js just sends the whole site to the client. So the client immediately [00:11:00] sees the guide, the client immediately sees the sidebar. And while the server waits for the response, the user's already interacting with the page. When the server gets the response, from sanity, it then streams the response to the client. And the client, once it's ready, it shows, the sanity results in the sidebar. That's all enabled what we did there. We have a data component, let's call it sanity sidebar, and that's wrapped in react suspense. And that really simple interaction of like suspense data fetching component is your way of telling server components, Hey, just stream this in when it's ready. in The meantime, you can show a fallback suspense, as you may know, gives you a way to show fallbacks, little loading spinners, stuff like that. Really simple code. And yeah, it enabled that really powerful pattern of bam, you're looking at your guides and then you got a little loading sidebar. And when it's done loading, you got your change log as well. Did you load that data in the root page of the site? Okay. Okay. So no matter what it would Well, Yeah, if initially, we did load the data in the route. That's how next just demanded that you load the data. But this is actually some of [00:12:00] the advanced patterns that we started unlocking as we started pushing the client server component boundary downward. We realized that if instead of Fetching the sanity data at the root, like you're used to in Next. js, if you fetched it in the component itself, then you could isolate that component and stream it in over suspense. Once we started getting more comfortable with that model, we started putting more of our data fetching in components rather than at the root. right before we dig into some more advanced methods and patterns, I just want to remind our listeners that this podcast is brought to you by log rocket. So if you want to spend more time building your great app and less time \ debugging, head over to log rocket. com today and you can check out some of their powerful tools such as trends so that you can find things happening in your app before you even notice them, heat maps and all sorts of other features that let you spend more time writing great code. So head over to log rocket. com today and check it out. So yeah, here we're talking about more advanced patterns that server components unlock for you. And we went over React Suspense, right? What's another [00:13:00] one that you and the team have stumbled upon and employed in your build? A question that I get asked a lot about our experience with server components is what happens if you want a client component to be the parent? Of a server component what happens if you want to mix server and client components? The reason this is difficult is I was talking about moving our client server boundary downwards. The reason this is difficult is because as soon as you have a client component, let's say I have, we were talking about the sidebar earlier, our sidebar has all sorts of rich interactions, you're opening these drop downs and everything that's a client component because it has to respond to user clicks. And as soon as I say, hey, this is a client component, I put the client directive at the top of that file, use client, as soon as I say use client, everything below that component in the tree becomes a client component. Everything that the sidebar imports is also a client component. Which is why it's easier to migrate and It makes a lot of sense from like a technical bundler perspective, but from a user perspective, what if I don't want to ship the entire [00:14:00] sidebar to the client, we were talking earlier about typography components, my typography components don't have to be shipped to the client. But I do need my typography components to render my sidebar. What happens if I want to keep my typography on the server? But ship the sidebar to the client. And that comes up really quickly in everybody's server component journey. How do you mix server and client components? If you can't import typography in your sidebar, then you have to do something else. You have to pass it as children or you have to pass it as props. So in, let's just say our layout, we have a layout component. It imports. Server typography and it imports client sidebar and then we have a sidebar is the parent and then we have typography as the child. Because layout is on the server, it can render typography and it inserts that typography into the client component. It's really interesting and complex and way over my head. I would love to talk to some React expert about how exactly it does that. If you want to mix client and server components, you [00:15:00] have to Import both in a server context and then just mix them using children or props. And it sounds like you guys found a happy medium with passing them as children. That was the preferred pattern. Yeah, passing as children or, in some cases if you needed um, React doesn't have a concept of slots, which some other languages or web components do. So if you want slots, if you want our sidebar, for example, we've got some buttons at the top and we've got some drop down items down below. We would have to pass those as props, not just children, because children is, one spot in the component. Meanwhile, with props, you can put it throughout the component. The code gets pretty hairy pretty quickly. We had to be very judicial with where we applied this, because that complexity impacts legibility, definitely. What about serialization? So does that component need to be serializable? Under the hood, that's how it works. The nice thing about The errors that they've been adding to Next. js and react the react canaries is that those kind of details [00:16:00] are good to know, but not necessary. Not a single time in the past year have I come up on an error. Where my component was not serializable. And I imagine that if my component wasn't serializable, I would have gotten an error. That's So we've talked a lot about, Darius coming from Old land into new land with RFCs. And that definitely provides great context for us to talk. About react server components. But what about the direction of somebody making an app for the first time, how do they wrap their heads around? I've made something in the past, and I'm making it now, do I start with only server components? Should I make it, you know, with client component heavy, because it doesn't matter, there's not a lot of performance impacts, because that's the whole pull of server components. Not the whole, but the big argument performance, right? Would you start from a server only land and then okay? Sprinkling client or do something similar to what you folks are doing right now? such a good question. I really am of two minds on this. [00:17:00] When I learned react, I learned it just a single page applications, create react app. And then I started realizing like, Oh man, like I need the SEO benefits and everything. I want this rendered on the server so that Google can read it and stuff like that. So I'm like, Oh, look, Gatsby exists. Oh, look, Next. js exists. So then I was introduced to server side rendering. And then. And then I started seeing kind of the limitations of server side rendering and right at the same time React server components arrived and I'm like this is great. I can now mix my components and everything. React server components are really the third tier of React complexity. You could ship everything to the client, you could start splitting stuff up with server side rendering, or you can unlock this deep functionality with React server components. Because of that flexibility, there's a lot of complexity and it takes a little bit of patience, a little bit of time to learn, to really wrap your mind around it. Because it's a lot to keep in your mind, especially when there are those legibility concerns that I mentioned earlier, should somebody just starting their application dive right in? I would say in your [00:18:00] own personal projects is a great place to learn when you have a tiny little. Case study and you can start seeing, , in your console logs, for example, Oh, that console log popped up in my terminal. So I know it's on the server that one popped up in my browser. I know it's on the client. I think it's a really good place to start experimenting and start realizing that there's so many terms around here, but at the end of the day you just need to know two things. Is this Component on the server, or is it on the client? I would encourage people to play with it, to wrap their heads around it and realize that there's a lot of scary stuff going on, but it's not that bad. At the same time, if you have teammates, if you have like contractors, if you have other people you need to work with, you really do need to consider the complexity and make sure that you have The communication and the technical power to make sure that everybody's on the same page and willing to go on that learning journey with you. Should production apps be moving to react server components? If you have a highly motivated and excited team, like we were lucky enough to have then absolutely, but for everybody else, take your time. Make sure [00:19:00] that these kind of things become institutional knowledge that everybody starts understanding the best ways to explain them. Like just recently on Twitter or X, I saw somebody start talking about the mixing of client and server components as not just the client or the component tree, but also the ownership tree and like these kinds of new concepts, these new ways of explaining server components are going to come out in the next year or two. If you're taking your team along. Either make sure they're the right team or let the industry figure it out for your personal projects. That's the place to learn. Do it. Yeah having that small Playground is like the best area to muck about and figure out what you know And what you don't know and like you said like having the console in a server and in the browsing just checking it Huge like it's huge for is you're like there it is. It's right there. I see it. It's on the server I'm a professional. I've been doing this for like almost 10 years and console log is still one of my favorite tools. of all time this question? We didn't really have slated on our You know, preemptive outline, but server actions. Next 14, right? [00:20:00] How does that shake up the model here? Because , you can just write a function with you server right inside of like your client component tree at any point, just import it. And you can await it like as your form action. So does that change the way you might structure your logic as components in your build? Yeah. This is the whole promise of server components, right? You learn this one simple thing and you start unlocking all these secondary benefits. You start getting to stream stuff in, you start getting to do server actions. We haven't used server actions in. Production yet. We just started because, stability and everything. They're not quite stable yet. And we learned the hard way that if you start using technology, that's not ready for production. It's going to be a lot of work. Of course we did. But now that server actions are stable, they're definitely something that people should be reaching for to replace their. Post APIs that they're using in forms and everything. The ergonomics are incredible. So many fewer lines of code. I understand there are valid concerns about mixing client and server logic and separation of concerns, but there are so many use cases where it's so nice just [00:21:00] to have Bam, here's my server action. Here's where I post this form response to our form collection, that kind of thing. So you definitely think it's going to shake up the way that. You might structure your client server component boundary Yeah, I can speak definitely from the perspective of somebody who writes a marketing site and a documentation site where our server interactions are relatively light. Where we need to handle form submissions. Where, store them to a database or store them to a service, where we might need to upload a video to mux video, that kind of thing. Those kind of light interactions where you're still mostly writing a front end website that only needs a few APIs, those are exactly where server actions are gonna really, the ergonomics are gonna be fantastic for front end developers who need a bit of server functionality, which has always been the promise of Next. js, right? You've always had your API routes where you can just write something light that your front end can interact with. on the topic of next 14. Because it's a light release, I guess you could argue like next 13 came out with sledgehammer and now next 14 [00:22:00] here is painting the trimming and stuff. So what in the next 14 release going from experimental and unstable to stable to your point, you only want to use stuff that's really stable when you're in the production setting. What are you picking from? What are you looking at? I was thrilled to see no new features. We survived Next 13 through frankly, its instability. The Versal team, the next GS team are incredible, but there really were a lot of bumps along the road in next 13. We were happy to adopt those bumps 'cause it was the right time. But I understand why so many in the community had so much trouble with. My favorite feature of next 14 is no new features, That's such a good tagline. It's so good to see that we broke out this new paradigm last year and I can't wait for it to really solidify and become stable. It's also true that Next. js is struggling right now to have a great dev and build experience because of the limitations of TurboPack's coming up and I've experimented a bit with [00:23:00] TurboPack. It's not stable yet. We're not rolling it out yet, but once it comes out, those improvements are going to be worthwhile. Finally, you only asked about one thing, but here I am talking about all three partial pre rendering. I'm still having trouble wrapping my mind around that, but the concept seems to be that I do nothing and more of my code gets rendered as HTML. That's great. The more the user sees as HTML while the JavaScript loads, the happier the user is, and obviously the happier I am. DArius, if people wanted to hear more about Mux and your journey over there with React Server Components, are you guys public about it at all, and do you have any other postings about your journey in converting to RSCs? Yeah. So if you don't have patience, Check out the Next. js conf talk. It's 10 minutes long. Somehow we squeeze it all down into there. If you do have patience I've never been known for being short winded. I wrote 5, 000 words on this , over at our blog. mux. com. I also wrote posts about our original docs conversion and about server actions. You can check those out. And finally like everyone I haven't [00:24:00] left yet. I'm still on Twitter that's, That's where you can learn more about server components and my journey and our journey with it. If you want to learn more about mux. com is the place to be Awesome. Darius, it's been a pleasure hearing about your journey converting to RSCs. , it's a nebulous world out there right now with everything on the horizon. So it's cool to see a real life implementation and hearing how it went. So thanks again for your time. gladly.