Composition vs Configuration: How to Build Flexible, Resilient and Future-proof Components

Rate this content
Bookmark

There are many ways of authoring components in React, and doing it right might not be that easy, especially when components get more complex. In this talk, you will learn how to build future-proof React components. We will cover two different approaches to building components - Composition and Configuration, to build the same component using both approaches and explore their advantages and disadvantages.

17 min
21 Jun, 2022

Video Summary and Transcription

Today's Talk discusses building flexible, resilient, and future-proof React components using composition and configuration approaches. The composition approach allows for flexibility without excessive conditional logic by using multiple components and passing props. The context API can be used for variant styling, allowing for appropriate styling and class specification. Adding variants and icons is made easy by consuming the variant context. The composition and configuration approaches can be combined for the best of both worlds.

Available in Español

1. Building Flexible and Resilient React Components

Short description:

Today, I will discuss how to build flexible, resilient, and future-proof components in React. We will explore two approaches: composition and configuration. Building future-proof components can be challenging as the complexity grows. Adding features like headers, variants, and icons requires careful consideration of props and styles.

Hey, today I'm going to talk about how to build flexible, resilient, and future-proof components in React. I'm going to cover two different approaches to building components, composition and configuration.

But first, let me tell you a bit about myself. My name is Tomasz Findly and I'm a full-stack web and mobile developer with 10 years of programming experience. I'm a co-owner of Findly WebTech, as well as a mentor and consultant at CodeMentor.io platform. Besides that, I'm the author of React, the Road to Enterprise and Viewed the Road to Enterprise books. I also write articles for Telerik and the Road to Enterprise blogs.

Okay, that's enough about myself. Now, components. So, let's be honest. Building future-proof components is not easy. I mean, well, if you have a component like this, it's very simple. So for this example, I will use an alert component. So for example, if you would want to have an alert component that would display a basic alert message, well, we could do something like this, right? We could receive text entries as props, have some divs with styles, and then render what was passed, right? And here's how we could use it. Just use the alert component and pass the text message inside of it.

So, obviously, that's very simple, but the problem with building good components is that, as we need to add more functionality, the complexity just grows, right? The code is becoming much harder to maintain and extend. So what if we would want to add more features to this alert component? Let's say we just want to add a header as well. So we could receive a header as a prop, and if one was passed, we would display it, right? As shown here in the example. So, for instance, on the right side, we have one alert just with and the other one with both the alert header and the text message.

So what about variants? Well, I guess we could again add another prop, like variant, right? And then based on that prop, and whatever the variant was selected, we could add appropriate styles. So, for instance, we could support variants like success, danger, warning, or info, as you can see on the right side. And that's how we would use it. Just pass header and variant props and some text inside.

So what about adding, maybe, an icon? Well, again, another prop, like icon. If it was passed, and we found a supported icon, then we can render it. Simple, isn't it? And that's how we could use it. So, basically, by default, the icon could be displayed on the left. But what if we want to specify on which side we want it to be displayed? Like, maybe not on the left, but on the right? Well, guess what? Another prop! So, for instance, we could pass a prop like iconPosition, right? By default we could set it to left. And then, if, for instance, it was right, we could specify some classes, right, that would be added on the alert container. Note that I'm using Tailwind here for classes.

2. Using Composition for Flexibility

Short description:

The configuration approach can become problematic when adding more functionality requires adding more props and conditional logic. It becomes harder to extend or overwrite the component's logic. On the other hand, composition offers a different approach. By using multiple components and passing props, we can achieve flexibility without excessive conditional logic. In the example, the alert component is composed of the alert wrapper, alert content, and alert body components, allowing for more control and easier extension.

And yeah, that's how we would use it, just more props. So, that was the configuration approach, right? Basically, whenever we just need to add more functionality, we add more props. But, well, that can be problematic at some point. Because the thing is, for every new variant and functionality, we need to add more and more props and conditional logic, right?

And sometimes it might just become much harder to overwrite the already defined logic inside of a component or even extend it. So, that's not really the best. The configuration approach makes it much harder. So, sometimes if a component can't be extended, we might need to build a new version of it. Well, as for pros, well, obviously a configuration approach, well, a component built with a configuration approach is quick and easy to use, right? Because you only need to know about what props are really available and what you need to provide. So, yeah. Basically different functionality and visual variants can be controlled via props and that's it. And another benefit of that is that it's much harder to diverge from the design system, right? Because the thing is you only can provide props and that's it. You can't do really anything else with it. So, well, this keeps the UI and behavior consistent.

But, yeah, like I mentioned the problem is that we can't easily extend the configuration build component or override it. So, what can we do? Well, I mean we could obviously provide maybe props like, let's say, a render icon, render header, you know, render body and so on and so on. But again, more props would be just more messy. So, there would be more conditional logic inside of the alert component. So, instead of trying to configure everything, how about we'll use a different approach, composition.

So, in this example, we have three components. First, the alert wrapper, then alert content, and alert body. And to the alert body, we pass the text message. I know three components just for the text message is a bit much, but stay with me. So, how it could look like? Basically, the alert component, obviously, it would receive some props. Then it would render a div with appropriate styles and children, right? So, in this case, the children would be the alert content and alert body. Then we have the alert content. As you see, it's very similar to the alert component. Because, again, it just receives the props. And has a div with some styles. And actually, well, the same will apply to alert body. I know there's a bit of repetition, and we have a few of the components already, but it's worth it at the end.

3. Using Context API for Variant Styling

Short description:

In this example, we can use different components to build the alert functionality, including custom markup. To provide variants to components like alertHeader and alertBody, we can use the context API. The variant is passed as a prop to the variant context provider, which then provides it to all components in the component tree. This allows for appropriate styling and class specification for the alert component. The context factory creates the context and consumer for easy consumption in other components.

Just remember that this is a contract example. So, yeah, that's how we use it. Now, if we would want to add, let's say an alert header, well, we don't need to add a prop. We just add another component, like alert header, basically. So, all these components are just building blocks which we use to compose the functionality.

And what about, let's say, yeah, so first up again, our header in this case also is very similar to other components. It just receives the props, renders the diff with correct styles and then finally the children. Now next, what if we would want to have variance? Well, I guess maybe we could actually pass one prop to the alert component. Of course, we could also create a component for it. But for this example, this will do. So we'd pass a variant prop to the alert. But now the thing is that we can't compose the components right, to build the alert functionality. But the thing is that we can also use not only the building blocks, the components that we build specifically for the alert, but we can actually just use any custom markup we want. If we really wanted it, we could not use any of these building blocks, just the alert component.

So how do we provide a variant to all these other components like alertHeader and alertBody? Because they need to know what's the variant. Because they need to have appropriate styles. For instance, the header and body for the success variant have a dark green text, while the background for the alert is light. And also, we have the border on the left that is dark. So what we can do is we use the context API to provide the variant that was specified to all the components in the component tree. So in this case, we receive variant as a prop, and then we pass it to the variant context provider. Besides that, we also use it to specify appropriate classes for the alert component. Now, let's have a look how we can build this variant context provider. So I've used a little context factory helper, we'll get to it in a moment. Basically, it just returns a custom hook that will consume the context and the context itself. And as you can see, one line five here, the use variant context is exported so that it can be consumed in other components. And then in the variant context provider, we basically just render the provider and pass the variant to it. And that's it.

Next. So here we have the context factory, basically, it creates the context, creates the consumer and returns them. Now, alert header.

4. Using Variants and Icons

Short description:

We update the alert header and body components to have access to the variant. We use the variant hook and consume the context to apply styles. Adding an icon is easy by using a pre-built component or custom markup. We use an icon config map to map icons to components. We consume the variant context to add appropriate styles to the icon. The composition approach is flexible and allows for easy extension of functionality. However, it requires composing building blocks and understanding their composition. The configuration approach only requires providing props.

Yes. So we need to now update the alert header and alert body component because they now also need access to the variant. So we import this variant hook, we consume the context and then we use it to apply appropriate styles. And we do the same in the alert body, again consume the context and apply the styles.

Okay. Next. So we have alert component with variants and icons, right. So if we wanted to add an icon, again, another building block, right, we can just use a component that was already built as part of the, let's say, you know, all the alert components and pass a specific icon. Or if you would want, we could even add some custom markup for it. Now, how would we use it? So first we can have an icon config map, where basically the icon is mapped to, let's say, sfg icons or whatever component you would want to use. Next, we use the icon prop to retrieve one of the icons that are supported. And then if we have one, then we render the markup for it. Besides that, we also consume the variant context so we can add appropriate styles to the icon, for example, correct colors. Like in our example, the success icon obviously will be green while warning icon will be orange. So as for positions, basically, we could move the alert icon component to the bottom and we could add some styles to it. For example, in this case, the alert icon, because it was moved to the bottom, it will be rendered after the alert content so it would be on the right side. And then we, for instance, can add some margin left auto with some right pad with some margin right value to have it positioned on the right side, as you can see here.

So that was the composition approach. So obviously the composition approach is extremely flexible, right, because you just basically use the components, which are building blocks, to compose the functionality. If you, for instance, need something more custom, you can just create a new markup, right, custom markup, or you can add more building blocks and that's that. So yeah, that's why the composition approach is really flexible. And it's not hard to, you know, extend functionality, or you don't even have to overwrite anything, right? Because you just compose everything. So, yeah, it is very easy to create different functionality and UI variants with composition approach. However, there are some cons to this approach, right? Because obviously you need to compose the building blocks yourself, right, to create this fully functioning component or feature. Because of that, you need to know how the building blocks work, what they do and how they should be composed. This isn't the case when using the configuration approach. Because with the configuration approach, you basically just need to know what props you're supposed to provide. And that's it. In the configuration approach, basically the component just takes care of everything, right, under the hood. Because you don't know...

5. Combining Composition and Configuration

Short description:

The composition approach offers more flexibility but requires more knowledge of how to compose building blocks. The configuration approach is less flexible but simpler to use and helps maintain consistency. Both approaches can be combined to get the best of both worlds.

You might not know what's going on there. You just need to know the props and what values you need to pass. That's that. While with the composition approach, you do need to know what the building blocks are doing and how they are... how they should be used. And well, obviously, another con is that it does take more time and code, right, to create the same thing. Because, again, you need to compose the building blocks yourself.

And another disadvantage is that it's much easier to diverge from the design system and ship inconsistent UI and behavior. Obviously, because you basically can compose the building blocks, you know, however you want, right. So it would be easier to make mistakes and ship inconsistent UI just, basically, by providing wrong classes or ordering the building blocks in the wrong way. This isn't the case with the configuration approach. Because, again, you just provide props and that's it.

So, again, both approaches have their pros and cons, right. So the question is, when should we use which one? Well, technically, why not both? Right, because what we can do is first use the composition approach to basically build the, well, create the components that are the building blocks, right, and then we can use them to create a configure component, like here we have an example. Again, like we did in the configuration approach example, we receive a number of props, right, for the alert, then we have the baseline component, which basically accepts the variant and class name. Then we also have the alert icon. If the icon was passed, then it'll be rendered. And alert content, where if the header was passed, then alert component would be rendered. And if the text for children were passed, then alert body would be rendered. And yeah, that's how you can combine composition and configuration approaches to basically build components.

So most of the time, just you can use a configured component, but if you need more flexibility, right, then you have these building blocks available for you. So in summary, composition approach offers more flexibility, but it does require more knowledge of how to compose the building blocks, right? And how they work. On the other hand, configuration approach is less flexible, but it is simpler to use and makes it easier to stick to the design system. But yeah, we can combine both approaches to get the best of both worlds. So you can find code examples for this presentation in this GitHub repo.

What's more, if you'd like to learn more about advanced patterns, best practices, techniques for many crucial concepts such as local and global state management, scalable project architecture, performance optimization, managing APIs, testing, and much, much more, you should definitely check out React.io's Road to Enterprise. And if you'd like to get in touch, you can find me on Twitter, LinkedIn, CodeMentor, and the Road to Enterprise platform as well. Well, that's it for today.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Remix Conf Europe 2022Remix Conf Europe 2022
23 min
Scaling Up with Remix and Micro Frontends
Top Content
Do you have a large product built by many teams? Are you struggling to release often? Did your frontend turn into a massive unmaintainable monolith? If, like me, you’ve answered yes to any of those questions, this talk is for you! I’ll show you exactly how you can build a micro frontend architecture with Remix to solve those challenges.
React Advanced Conference 2021React Advanced Conference 2021
47 min
Design Systems: Walking the Line Between Flexibility and Consistency
Top Content
Design systems aim to bring consistency to a brand's design and make the UI development productive. Component libraries with well-thought API can make this a breeze. But, sometimes an API choice can accidentally overstep and slow the team down! There's a balance there... somewhere. Let's explore some of the problems and possible creative solutions.
Remix Conf Europe 2022Remix Conf Europe 2022
37 min
Full Stack Components
Top Content
Remix is a web framework that gives you the simple mental model of a Multi-Page App (MPA) but the power and capabilities of a Single-Page App (SPA). One of the big challenges of SPAs is network management resulting in a great deal of indirection and buggy code. This is especially noticeable in application state which Remix completely eliminates, but it's also an issue in individual components that communicate with a single-purpose backend endpoint (like a combobox search for example).
In this talk, Kent will demonstrate how Remix enables you to build complex UI components that are connected to a backend in the simplest and most powerful way you've ever seen. Leaving you time to chill with your family or whatever else you do for fun.
React Advanced Conference 2022React Advanced Conference 2022
29 min
Understanding React’s Fiber Architecture
Top Content
We've heard a lot about React's Fiber Architecture, but it feels like few of us understand it in depth (or have the time to). In this talk, Tejas will go over his best attempt at understanding Fiber (reviewed by other experts), and present it in an 'explain-like-I'm-five years old' way.
React Finland 2021React Finland 2021
36 min
The Eternal Sunshine of the Zero Build Pipeline
For many years, we have migrated all our devtools to Node.js for the sake of simplicity: a common language (JS/TS), a large ecosystem (NPM), and a powerful engine. In the meantime, we moved a lot of computation tasks to the client-side thanks to PWA and JavaScript Hegemony.
So we made Webapps for years, developing with awesome reactive frameworks and bundling a lot of dependencies. We progressively moved from our simplicity to complex apps toolchains. We've become the new Java-like ecosystem. It sucks.
It's 2021, we've got a lot of new technologies to sustain our Users eXperience. It's time to have a break and rethink our tools rather than going faster and faster in the same direction. It's time to redesign the Developer eXperience. It's time for a bundle-free dev environment. It's time to embrace a new frontend building philosophy, still with our lovely JavaScript.
Introducing Snowpack, Vite, Astro, and other Bare Modules tools concepts!

Workshops on related topic

DevOps.js Conf 2024DevOps.js Conf 2024
163 min
AI on Demand: Serverless AI
Featured WorkshopFree
In this workshop, we discuss the merits of serverless architecture and how it can be applied to the AI space. We'll explore options around building serverless RAG applications for a more lambda-esque approach to AI. Next, we'll get hands on and build a sample CRUD app that allows you to store information and query it using an LLM with Workers AI, Vectorize, D1, and Cloudflare Workers.
React Summit 2023React Summit 2023
137 min
Build a Data-Rich Beautiful Dashboard With MUI X's Data Grid and Joy UI
Top Content
WorkshopFree
Learn how to put MUI’s complete ecosystem to use to build a beautiful and sophisticated project management dashboard in a fraction of the time that it would take to construct it from scratch. In particular, we’ll see how to integrate the MUI X Data Grid with Joy UI, our newest component library and sibling to the industry-standard Material UI.
Table of contents:- Introducing our project and tools- App setup and package installation- Constructing the dashboard- Prototyping, styling, and themes - Joy UI features- Filtering, sorting, editing - Data Grid features- Conclusion, final thoughts, Q&A
React Summit 2022React Summit 2022
147 min
Hands-on with AG Grid's React Data Grid
WorkshopFree
Get started with AG Grid React Data Grid with a hands-on tutorial from the core team that will take you through the steps of creating your first grid, including how to configure the grid with simple properties and custom components. AG Grid community edition is completely free to use in commercial applications, so you'll learn a powerful tool that you can immediately add to your projects. You'll also discover how to load data into the grid and different ways to add custom rendering to the grid. By the end of the workshop, you will have created an AG Grid React Data Grid and customized with functional React components.- Getting started and installing AG Grid- Configuring sorting, filtering, pagination- Loading data into the grid- The grid API- Using hooks and functional components with AG Grid- Capabilities of the free community edition of AG Grid- Customizing the grid with React Components
TypeScript Congress 2023TypeScript Congress 2023
131 min
Practice TypeScript Techniques Building React Server Components App
Workshop
In this hands-on workshop, Maurice will personally guide you through a series of exercises designed to empower you with a deep understanding of React Server Components and the power of TypeScript. Discover how to optimize your applications, improve performance, and unlock new possibilities.
 
During the workshop, you will:
- Maximize code maintainability and scalability with advanced TypeScript practices
- Unleash the performance benefits of React Server Components, surpassing traditional approaches
- Turbocharge your TypeScript with the power of Mapped Types
- Make your TypeScript types more secure with Opaque Types
- Explore the power of Template Literal Types when using Mapped Types
 
Maurice will virtually be by your side, offering comprehensive guidance and answering your questions as you navigate each exercise. By the end of the workshop, you'll have mastered React Server Components, armed with a newfound arsenal of TypeScript knowledge to supercharge your React applications.
 
Don't miss this opportunity to elevate your React expertise to new heights. Join our workshop and unlock the potential of React Server Components with TypeScript. Your apps will thank you.
React Summit 2022React Summit 2022
50 min
High-performance Next.js
Workshop
Next.js is a compelling framework that makes many tasks effortless by providing many out-of-the-box solutions. But as soon as our app needs to scale, it is essential to maintain high performance without compromising maintenance and server costs. In this workshop, we will see how to analyze Next.js performances, resources usage, how to scale it, and how to make the right decisions while writing the application architecture.
React Summit 2023React Summit 2023
31 min
From Idea to Production: React Development with a Visual Twist
WorkshopFree
Join us for a 3-hour workshop that dives into the world of creative React development using Codux. Participants will explore how a visually-driven approach can unlock creativity, streamline workflows, and enhance their development velocity. Dive into the features that make Codux a game-changer for React developers. The session will include hands-on exercises that demonstrate the power of real-time rendering, visual code manipulation, and component isolation all in your source code.
Table of the contents: - Download & Setup: Getting Codux Ready for the Workshop- Project Picker: Cloning and Installing a Demo Project- Introduction to Codux Core Concepts and Its UI- Exercise 1: Finding our Feet- Break- Exercise 2: Making Changes While Staying Effective- Exercise 3: Reusability and Edge Case Validation- Summary, Wrap-Up, and Q&A