Hello everyone 👋
I am going to tell my experiences about designing APIs of the reusable react components
library. In some cases, the decisions were simple to go but in most cases, we (my team) needed to account for multiple factors while deciding on API structures, composability
and configurability
were the major two points we needed to emphasize upon the most.
I am trying to bring out the learnings as multiple posts while targeting small problems we faced, this post is the first post of its type from me.
We, a team of three developers and three designers have been working on creating a design system. For the same, we started developing the reusable react components as an open-source library to make them more shareable and reusable. Starting with simple components following atomic design
principles it was going well, the problem of deciding on APIs to be configurable or composable started coming from some molecular level components.
While working on the library for about 14 months we released that we can't go with one single approach of having the component's APIs configurable or composable, instead, we need to have a mixed approach to keep the consistency in place and provide enough surface area for customizations.
For an instance I will take you through the Card
component API design approaches, we can have a Card
component that takes a string as a header prop and renders it in the desired way.
// using
<Card header="This is card title">
This is card body.
<Card>
// inside component
<div>
<Header>
{props.header}
</Header>
<div>
It is great for consistency, but we need to provide some composability as the user may need to have a button, icon, badged or subtitle, or any other component in the header of a card which is not possible currently as the header is treated as a string only.
Now we have multiple options to do that.
- Making the
header
prop type react element.
<Card
header={
<>
This is card title
</>
}
This may seem good at first thought but is annoying for developers because the major use-case is to use string only and we need to wrap the heading like a component.
- Making independent
CardHeader
component.
<Card>
<CardHeader>
This is card title.
</CardHeader>
This approach provides most composability but again this has the same problem of using the independent component for just a title string in most use-cases.
- Making
CardHeader
component part of Card component.
<Card>
<Card.Header>
This is card title.
</Card.Header>
Exporting the sub-components as property in parent component like Card.Header
removes the need to import component independently, other than that is is exact as independent CardHeader
component.
Deciding factor:
My personal preference is CardHeader
as it brings the same amount of consistency in components in all use-cases and keeps the composable part un-restrictive, also it brings consistency in component's APIs as we can have other subcomponents with similar APIs, like CardFooter, CardBody, etc.
However we are considering option one as it makes API simple for frequent use cases and for composition we can use the custom components as Card's child component since Card renders its children components as it is and also because our internal teams poll resulted in liking it.
<Card
header="This is card title."
footer={
<>
<Button>
Card Action
</Button>
</>
}
>
// for composition
<Card>
<CustomHeading>
<Badge>
Disabled
</Badge>
<Text>
This is card title.
</Text>
</CustomHeading>
This is card body.
This process and practice are exhaustive in itself, I am trying to get together all the learnings and knowledge for API design and compose a decision framework (document). I will post the same as soon as it gets completed.
Also for this and other such small tricky decisions, I have created a poll, Please feel free to share your feedback on it.
https://forms.app/satyamyadav/react-component-api
Here you can find our component library on github, please feel free to star it.
https://github.com/innovaccer/design-system
Thank you !! 🙏
Please share your feedback on this post to help me improve.
Top comments (0)