Channel
Interviewed Person
Fernando Rojo
Composition is React’s most powerful tool, but it’s also one of the most misunderstood. At React Universe Conf 2025, Fernando Rojo, Head of Mobile at Vercel, demonstrates how composition can scale your codebase from a single component to thousands, while making it easier for both humans and AI to work in. Skip the intro ⏩ 0:39 🔹 Why “boolean prop hell” (e.g. `isEditing`, `shouldRenderButton`) makes components unmaintainable. 🔹 How real-world UIs like Slack’s composer quickly become messy when built with conditional props. 🔹 A compositional approach that replaces bloated monoliths with small, flexible components. 🔹 Techniques for handling state management, whether ephemeral (local) or global (synced across devices). 🔹 How composition makes codebases more AI-friendly, enabling developers to pair program effectively with AI tools. 🔹 A special live AI prompt to close the talk. If you’ve ever wrestled with sprawling conditionals and rigid components, this talk will help you fall in love with React again. See you at another React Universe Conf 🎟️ https://clstk.com/4m4dxn4 Follow React Universe Conf on X to stay up to date 🐦 https://x.com/reactuniverse_ Check out Callstack content 📚 https://clstk.com/41IqFHl
[Music] [Music] This conference is brought to you by Callstack, your React and React Native development experts.
[Music] Is this as good as it gets? You build a form for creating a user and it works great. And then down the line, you need a form for updating user. So you add a boolean prop to your generic user form is update user. But update
user has some differences. It sets the initial state to the existing user and it shouldn't render the welcome message or terms and conditions. So you pass a boolean for each of those to hide them. Oh, and uh when the mutation succeeds, it shouldn't redirect to onboarding. And then it happens. You need a component for only updating the user's name. So maybe you create an update user
name component and you pass only edit name as a prop. And maybe you need to set is slug required to false. And I know if you've written React for long enough, you have a lot of code that looks like this. And I'm sure you don't like peeking inside of user form because it's a complete mess. And the question is, is there a better way? And to illustrate, I'd like to examine a complex UI component that we're all familiar with. Slack composer.
How would you build this? Now, I don't have any idea on how they actually implemented it, but whenever I use a product this often, I can't help but wonder. How do they organize this UI? And how do they handle the state management? On the surface, this component may seem simple, but it comes in many forms. It's used in channels and DMs, DM threads, channel threads, forwarding a message, editing a message, and a few more.
Each implementation has its subtle differences from logic and UI. For most of the composers, the state is actually global and as you type, it's synced across devices. But for one like forwarding a message, it's actually ephemeral. Now, over the years, I've seen hundreds, maybe thousands of React code bases, articles, and tutorials. I have seen your code, and I've seen what AI outputs when you ask it to build with React. So,
to start, I'll show you what I think the typical approaches to building a composer like this. And by the end, I hope you'll come away with a more enjoyable way to write React. One that makes both humans and AI agents productive, even as a codebase gets larger. As you build a composer, you'll probably start with something like this. You have a header, input, and footer. And in your channel, you simply render it and pass it on. And it looks simple enough. And so our composer for channels
is complete. But what about sending messages in a thread? Well, I'll create a thread composer and I'll just swap out the submit function and we're good to go, right? But wait, I know you've seen this. The channel and the thread have this tiny difference. The thread composer has this little bar here which says also send to channel. When you check that box, it also sends your message back to the original channel. We have to account for this in our