Designing Reusable React Components

A personal, opinionated take on designing components in a world of simple scalable reuse.

Fernando Doglio
Bits and Pieces
Published in
7 min readJan 20, 2020

--

Image by Bessi from Pixabay

Using ReactJS to build your projects brings many benefits, but one very obvious one is the implementation of the component pattern. Thanks to the modularization that it brings to your code, you’re able to import and use external components into your project with very little effort.

That is, of course, if the desired components have actually been designed in a way that makes them truly reusable, otherwise, your attempt to avoid reinventing the wheel will cause you a great deal of pain.

So, in this article, I’ll demonstrate how to design and test your components to make sure they’re truly reusable and shareable.

Designing for reuse is getting more useful

Today, and thanks to tools like Bit, reusing components becomes a simple choice. Using platforms like Bit.dev you can have every component available in a shared reusable collection, with very little overhead.

Here’s a short example:

Let’s say you’re working on a React to-do app, just like the one shown below, and you want others to use and develop its components in other projects.

React app with components
// clone the project
$ git clone https://github.com/teambit/react-demo-app.git
$ cd react-demo-app
$ yarn
// install Bit & initialize a workspace in the project's directory
$ yarn global add bit-bin
$ bit init --package-manager yarn
// login (after creating an account and collection in bit.dev)
$ bit login
// add all components
$ bit add src/components/*
// configure a compiler (to decouple the components from your env)
$ bit import bit.envs/compilers/react --compiler
// tag & export
$ bit tag --all
$ bit export user-name.collection-name
// take a few mintues to write examples for them in bit.dev and you're done :)
The To-Do app’s shared components

Easy cross-repo collaboration

If cross-repo collaboration brings to your mind authoring and consuming externally packaged bundles of code — you’re thinking of the wrong thing.

This is not collaboration. This is pretty much a producer-consumer relationship between the publisher of that external bundle of code and the installer of the package.

What if you could extend the publisher’s work, expand and even fix some bugs? The traditional publisher-consumer dynamic, would not allow for that. In fact, if you wanted to collaborate like that, you’d have to either submit a well documented Pull Request (that is, if the repository is public and the author allows for such a thing) or fork their repo and make it your own (thus instantly gaining a whole new repository of code to maintain).

With Bit, however, you can easily extract components from your main repo while building your project or you can install external code into your project that would act as an integral part of your codebase. You can then modify that code as you see fit, and if you need to submit those changes, you can update the registry (directly, with the required permissions or indirectly, with a PR).

Let me say that again: you don’t need to switch projects in order to contribute to those external dependencies. This is a layer of abstraction on top of Git, not below or even at the same level, which simplifies management considerably.

let’s say you’ve imported the removable-list-item component from our previous collection to a new project, and you’d like to update it (in your local project as well as in the shared collection):

// create an example project & initilize a Bit workspace
$ npx create-react-app example-app
$ cd example-app
$ bit init
// import the component
$ bit import user-name.collection-name/removable-list-item
// modify the component located at components/removable-list-item/RemovableListItem.js - notice it is in the root directory, not in the "src" directory)// tag & export it back
$ bit tag user-name.collection-name/removable-list-item
$ bit export

Maximizing code reuse…

…and minimizing the time and effort spent on making your components reusable.

This is a by-product of the previous benefit. By being able to perform cross-repository contributions, you’re able to organically grow your (and other’s) component collection, based on your actual needs.

Let me ask you this: which set of components do you think will be best designed and architected? A whole UI library that was designed from scratch in an “artificial context” or a set of components that are being built in a real context and in response to a real need? Building your components inside your current project guarantees they are adopted and used.

Designing context-agnostic components

Here, I discuss two domains that need to be addressed when designing reusable React components: styling and states. It is far from being a full objective discussion on that subject. It is nothing more than a tiny opinionated sample of it.

Wrong styling

While your JavaScript must be modular and re-usable your components will also need to have CSS styles associated to them and if you’re writing your own stylesheets for them (which makes sense) you need to remember to also make that modular and reusable.

For example, inline styles like below are a bad idea:

Although that code works and it would show you what you’d expect to see: a hot-pink button:

You’re literally publishing a component that can’t be visually modified unless someone messes with your code. That’s not the point of these components. As a general rule of thumbs, you’d want anyone including your components on their code to worry about customizing the visual aspects while at the same time trusting your code so much they can ignore it (think about it, how many times did you open the React files to review their code?).

So in other words, you’ll want to give your users a way to customize the visual aspects of your components, either by passing personalized props or by providing some specific CSS classes they can use. Personally, I prefer the latter, since that keeps the visuals and the logic separate and your HTML code stays clean and consistent with the rest of your markup.

The following code shows how easy it is to setup a prop that’ll help you choose the CSS class:

You can see how I’m importing the CSS file which will contain the visual styling.

That way, you can use the component like this:

Now your markup makes semantic sense since you’re just defining a buttong and a big button (whatever that means). And at the same time, you can customize the classes by adding to them in your own stylesheets if you decide to add the component on your code.

Using global state

Relying on the global state to communicate information between components is a common pattern and now thanks to useContext and the createContext API, we’re able to easily use this concept to simulate the good ol’global state (and before that, Redux was used).

That being said, you can imagine how counter-productive such a patttern can be when trying to create a component that others can reuse. When designing your components in a way that they can be extracted and exported from your application directly into someone else’s code, they’ll need to be independent and should not have such a dependency.

Mind you, Bit will let you create a single package with all the dependencies needed, and the component will work, but your users will need to adapt their code to use the same global state, which might, potentially, break their entire architecture.

Consider the following example from React’s main documentation:

Imagine wanting to extract the ThemeTogglerButton component out so others can use it. We can do so easily with Bit using the following lines:

$ bit init # <-- this will initialize a Bit workspace in your project's directory$ bit add src/theme-toggler-button.js # <--- this will prepare the component to be exported$ bit status # <-- let's check how we're doing

The output from that command should be something like:

That’s right: issues found, Bit is has recognized our components have dependencies. We could potentially add those extra files to be exported with our component, but as I said before, that would force whoever is trying to use our ThemeTogglerButton to also use that global context.

Now consider the following version of the same component:

This time around, the component has no external dependencies, especially none that will break other app’s architecture. We’re simpliy using a local state to store the theme and then toggle it locally.

This time around, using Bit to check our status will return the following output:

Success! We’re now ready to continue with the export process!.

Conclusion

Sharing components across repositories has been made simpler thanks to tools like Bit but if they’re not designed to be context-agnostic (by making the right decisions in terms of styling and state management), they would not be usable in other environments.

Learn More

--

--

I write about technology, freelancing and more. Check out my FREE newsletter if you’re into Software Development: https://fernandodoglio.substack.com/