Component Design Patterns
The following are a few useful component design patterns. I am not the inventor of any of these patterns, but I hope this article can serve as a useful reference.
I favor React and Ember because those are the two frameworks with which I am most familiar. But, the principles are relevant to other component systems as well.
This article is also strongly biased towards web client development, but I acknowledge that the component abstraction is more generally applicable and useful.
Widget components are black-box components that accept context-specific configuration and that can ostensibly be added with just a line or two of code. Think a Facebook Plugin, a stock ticker widget, or a jQuery plugin or UI widget.
Widgets are useful for organizing bundled UI, state management, and/or network requests, and can be effectively reused as long as they require only limited contextual configuration and don’t need to be otherwise extended.
Widget component benefits
Widget components can be useful for reusing and organizing code.
What makes widget components appealing as a means of reusing code is how simple it is to reuse them. That is, with just a little bit of code you can add a lot of functionality. However, they can be tricky to customize. (See gotchas for additional explanation.)
Widget components are also useful for organizing large blocks of components and elements. I have heard them referred to as implementation components since they are often effectively wrappers for bundling and coordinating interactions among inner components and elements.
In the project I am currently working on we organize our application into widget components that we refer to as page components and page fragment components. These components break the application into more manageable and testable chunks.
Widget component gotchas
Widget components are tricky to customize. If you have access to the underlying code you can add additional configuration options. But, extensive configuration (whether implicit or explicit) can make the code convoluted and brittle.
Massive configurations are also tricky when including a widget component. For example, if you are building a web client then you are likely using some superset of HTML to describe your application. Complex configurations are effectively DSL’s that lack the expressiveness and niceties provided by whatever templating system you are using. (See Compound components for an example of how to write components that leverage your templating system for customization.)
If you need to customize a widget component and you don’t have access to the source you effectively need to extend the widget. Inheritance is a less ideal approach to customization, and if you end up mucking around with the private internals you may affect the widget in unforeseen ways, or you could make it harder to upgrade the widget in the future.
React example: https://www.youtube.com/watch?v=hEGg-3pIHlE
React example: https://github.com/smfoote/pure-typeahead
Ember example: https://ember-twiddle.com/cca9ab9ff3fa75efc1d84c7cc8b652e9
Compound components are a return to the goodness of markup languages. Think HTML lists or forms. Summarily a compound component is a collection of related, composable components, the compositions of which provide coordinated layout and/or interactivity and state.
Compound components are useful for sharing highly customizable patterns of layout and interactivity. Again, think HTML lists and forms.
Compound component benefits
Compound components are flexible and customizable, and as a result can be highly reusable across a broad set of contexts. They embrace the templating language and leverage composition.
Compound components are most successful when you are able to extract the essence of a given pattern.
My current employer makes extensive use of compound components to provide a library of common UX components to a diverse ecosystem of web clients.
Compound component gotchas
What tends to scare people about compound components is that they can be at least ostensibly more verbose than widget components. (Such comparisons tend to discount the cost of configuration of widget components.)
Compound components are also by their nature more flexible. And there are occasions where folks avoid compound components to deliberately limit flexibility.
React example (using HOC): https://redux.js.org/docs/basics/UsageWithReact.html#presentational-and-container-components
React example (using a render prop): https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce
Ember example: https://emberway.io/skeleton-screen-loading-in-ember-js-2f7ac2384d63
I am going to abuse the original definition of container components, and instead use the designation to refer to the class of components that aren’t directly concerned with presentation (e.g. DOM). Container components are instead concerned with things like data, network requests, state management, and action handling.
The following are just a few examples of how container components can be used:
- Exposing data to templates from the store, API, services, etc…
- Exposing actions to templates that when triggered initiate store updates, API requests, service method calls, etc…
- Managing client side state (e.g. a state machine)
- Normalizing or decorating data
Container component benefits
Container components provide a separation of concerns.
This separation of concerns facilitates code reuse. For example, you may expose user session related data and actions multiple places in the UI, but the presentation may not always be consistent. A container component can be used to expose the common data and actions.
Container component gotchas
If you aren’t careful you can inadvertently diffuse your application logic throughout many components making it challenging to understand and reason about. Systems like Redux consolidate and organize application logic, and use container components simply to expose data and actions to the presentation layer.
Disclaimer (the problem with ontology)
The definitions above are deliberately vague, and in practice components often incorporate aspects of multiple patterns.
I won’t even try to define what a component is.
The jQuery Bias
For a while there in web development jQuery and comparable DOM manipulation libraries were the go-to tools for creating ambitious web clients. Not saying you couldn’t apply design patterns similar to the ones described above when working with jQuery plugins, but the bias was towards configurable widgets.
That bias seems to have carried over into component development, and many components are unnecessarily reminiscent of plugins.
I don’t mean to discount how revolutionary and helpful jQuery and comparable libraries were, but the plugin abstraction is foreign to the web.
The inspirations for components are HTML and the DOM. Components are an invitation to better understand what makes HTML and the DOM so useful and powerful, and then to embrace and augment rather than obscure.