Writing elegant and resilient UI components
As a Front-end developer, building resilient and reusable components is a top priority of mine, and there are so many arguments in favor of well thought out components.
Thankfully, most of today's UI libraries and frameworks go a long way to help you build the best components possible for your projects (and more importantly, for your team). Nevertheless, keeping a few guidelines in mind can help us avoid pitfalls, especially when it comes to large-scale applications.
In this article, we'll go through concepts I follow everyday that are library and framework agnostic, meaning they apply to UI components as a whole.
- Have a modular approach
- Name your components well
- Keep your props simple
- Keep your business logic in container components
Have a modular approach
Ideally, your components should follow the FIRST principle:
- Focused: one component, one responsibility, if a component is doing too much, ask yourself if you can extract that logic somewhere.
- Independent: ideally, a component should not depend on another one to function. Passing simple and straight to the point props can help you create independent elements. If you've ever used Storybook, think of it that way: Can I extract this component into a story easily?.
- Reusable: UI components are lego bricks, they should fit anywhere pretty easily. Once again, a component's reusability is often determine by the simplicity of its props (more on that topic later).
- Small: I was horrified to see components reaching the 1000 lines mark on a project I'm currently consulting on. Keep ๐ them ๐ small. A small component can be read and explained easily and is simpler to test.
- Testable: How much mocking is required to test this component? is usually a good question to ask yourself, complex components will require a complex context to mock beforehand. Keeping in mind that the easiest components to test are known as pure components, meaning that for a given input, the component will always render the same output, produces no side effects and relies on no external mutable states.
Of course, you'll be working on elements that are truly dependant on your business logic, meaning you probably won't be able to follow these guidelines completely, and that's okay. Some components aren't meant to be reusable and some components won't be independent; but keeping this principle in mind is a good start.
Name your components well
I tend to try and keep my component names short, meaningful and easy to pronounce.
Here are some good and bad examples:
1<!-- Good --> 2<user-button></user-button> 3<payment-details></payment-details> 4<user-card></user-card> 5 6<!-- Bad --> 7<user-btn></user-btn> 8<!-- hard to pronounce --> 9<user-guarantee-payment-tab></user-guarantee-payment-tab> 10<!-- too long --> 11<ui-dropdown></ui-dropdown> 12<!-- every component is a UI element, no need to mention it --> 13
Keep your props simple
As mentioned in the first tip, props can make or break a component.
- Avoid passing down complex object structures, favour individual attributes as props whenever possible
- Use simple names for your props. We should be able to understand its purpose (even partially) upon reading it
Basically, try using Javascript primitives (strings, numbers, booleans) and functions as props whenever possible.
Keep your business logic in container components
Container components (such as layouts) should take care of computation and business logic as a whole in order to pass its results as props to presentational components.
This pattern isn't always valid, but the idea it promotes is important to keep in mind: complex and stateful logic is easier to maintain when kept separate. In the case of a React application for example, this business logic can be extracted in a custom hook, so this "smart/dumb" component pattern really is about separation of concern.
Often times, having each component handle their own logic can lead to them being hard to re-use throughout your application as they will be bound to a specific context.
Don't overdo it
These are just general tips for building efficient components. Of course, each project has different requirements and may not allow for you to follow these guidelines all the time.
As Dan Abramov says in his Writing Resilient Components article: Donโt Get Distracted by Imaginary Problems. Keep in mind that it's not worth it to over-engineer all of your components and to enforce rules that may not bring meaningful differences.
I hope this short list will help some of you build better UI components in your day-to-day. As always, if you have any questions, tweet at me @christo_kade โค๏ธ