Skip to content

@wordpress/components: high-level API strategy for compound components #63704

Open

Description

Context

As we're re-writing some of the components in the @wordpress/components package to use ariakit, we can take advantage of the low-level and granular nature of the library components, and build our components with the desired level of abstraction:

  • at one end of the spectrum, we could offer a very low-level and modular set of compound components (basically 1:1 to ariakit);
  • at the other end we would expose high-level monolithic components (similar to most components in the @wordpress/components package).

Each approach has pros and cons: monolithic components offer a pre-packaged, easy-to-use solution to consumers, but are very inflexible, and often end up causing severe YAPping and style overrides; low-level components are flexible, but are more difficult to use and offer little control and "art-direction" to the library.

We need to pick the compromises that we think suits best the role of the package, and decide how to mitigate the negative aspects of that choice.

Our shared approach so far has been to find a middle group:

  • Expose compound components, but try to keep a minimum level of abstraction
  • Potentially expose, alongside the "compound" version, a monolithic component representing the simpler and most frequent usage of the component (this aspect should be decided case-by-case and may be also influenced by backward-compatibility reasons).
  • Use Storybook as a place to provide ready-made "recipes", ie. example usages of the compound components that fulfill a certain UI that can be copied and dropped in a codebase with little-to-no tweaking.

What

The tricky aspect when "abstracting" lower-level components into higher level, is that there can be a confluence of originally "separated" APIs in one place. This aspect started emerging when working on components that have both a trigger and a related popover, like dropdown menu or select controls (see example conversation).

We need to find ways to make sure that our APIs are flexible and future-proof and are clear to our consumers. For example:

  • how would we make sure that consumers can pass props and grab refs or specific subcomponents, like the root wrapper vs the trigger vs the popover wrapper?
  • do we want (and can we) achieve a certain level of control (like discussed here about a potential Dialog component)

A few potential solutions:

  • lower the level of abstraction of our compound components, which would avoid the problem altogether, but would also introduce a different set of compromises;
  • use render props
    • for example, in DropdownMenu we are using a trigger render prop for the trigger button, and the children prop for menu items. Any aspect related to the trigger is passed directly by the consumer to the component rendered in the trigger prop;
    • in a hypothetical Dialog component, we could have use render props as "slots" in order to restrict the design and/or behavior of the component if needed.

Any thoughts?

cc @WordPress/gutenberg-components

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions