Component API Design

guidelines for building shared components

Photo by Daniel McCullough on Unsplash

Overview

  • Make the right choices easy
  • Make incorrect choices obvious
  • Provide helpful off-ramps

Make the Correct Choices Easy

Interactions should be first intuitive, then discoverable, and last standardized. *

* Summarized from The Design of Everyday Things by Don Norman

Note: While there is a hierarchy of preference, these qualities are also interdependent. We should design our components to be intuitive and discoverable and standardized.

Intuitive

Shared APIs across common components

Creating shared APIs across components reduces the amount of context needed users to become proficient with the library. While more complex components will have inevitably have more component-specific props, there are ways to consolidate them. For example:

This Accordion component uses a component-specific object to structure its contents.

High-level Accordion Component Example

FeatureList also has its own component-specific object, but it uses the same config prop.

High-level FeatureList Component Example

Lower-level components will have more common props that can be used to modify them. For example:

This is a standard Button using the danger variant to modify its styling.

<Button variant="danger">Cancel Your account</Button>
Button Component Example

Alert also uses the variant prop and danger value.

Alert Component Example

Leveraging existing APIs

We can also leverage familiar, existing APIs to reduce cognitive load. For example:

This AccordionTitle component uses React’s children prop to render child components (as opposed to using a custom content or title prop. This might seem like a minor point, but I've found that as your component library grows, remembering one-off prop names such as title becomes much more challenging.

Accordion Header Component Example

We can also leverage familiar CSS style attributes and HTML attributes to reduce context:

Text and Button Example Component Examples

Discoverable

Note: While there is a preferred hierarchy, each is interdependent. We should incorporate self-disclosure, documentation, and readable source code into our library.

Self-disclosure

This is the most preferred option for discoverability. Components self-disclosing or hinting at their API provides guidance in the user’s immediate context. There’s no need to hop into external documentation or source code to figure out how to interact with them.

TypeScript and IDE plugins provide helpful mechanisms that aid self-disclosure. Whenever possible, we should leverage these options first.

Documentation

Along with self-disclosing component API, we should provide external documentation. Component docs are intended to support direct-users (engineers and designers) They should include information for using the API, but also higher-level information about when to use the component.

Source code

Source code should be a last resort for discoverability. If users are regularly digging into code to understand how to use a component, that’s a flag for poor design. However, these situations do occur, and we should plan for them by optimizing readability. If unintuitive choices had to be made or a temporary patch had to be employed, we should add a comment alongside that code. We should also file an issue for that problem if one does not already exist. Another way to make source code more readable is a standard file structure, which will be discussed in another post.

Standardized

Make incorrect choices obvious

Type-checking

Consistent styling patterns

Deprecation warnings

Provide helpful off-ramps

If you don’t provide helpful off-ramps, people will make their own.

Handling edge cases

  • Components become rigid and brittle as they support more cases
  • Components become more challenging to maintain and update
  • Component APIs become more opaque to users

Put simply, too many off-ramps create chaotic driving conditions and only increase maintenance costs. But there will always be edge cases we don’t anticipate, and people will always attempt to make their own off-ramps. This isn’t because they are poor drivers or have malicious intent. The road was not designed to accommodate their situation.

How can we design a solution to handle these unanticipated cases?

Composability

Composable Accordion Component Example

Clear communication

Thanks for Reading!

design systems @workday