At the most basic level you can ship basic HTML elements that match your style guides (labels, text fields, radio buttons, etc.). Sure, developers can use those individual pieces to make their own UIs, but they’re going to have to. piece everything together themselves. Think about the form elements Insed as an example earlier, specifically text fields and radio buttons – do you ever see those by themselves? Of course not – they’re paired with labels so you know what you’re supposed to be entering into the field. Those labels usually have some form of visual indicator if a field is required, so if all you’re exporting is individual elements developers will have to wire those 2 pieces together. A better option would be to ship form components that come with all the elements needed to function as a self-contained unit, such as labels and a required flag that updates both the label and enables validation if true.
Let’s think bigger than simple form inputs for a moment. Think about common user behaviors across all your apps and services. Are they regularly performing searches? What about bulk updates? These are pretty complex user interface elements, but if your users are interacting with variations of the same interface in different sections of your application, they’re exactly the sort of thing you want to export as a single component. Just giving developers basic UI components and telling them to implement everything themselves is inviting a bunch of pages with a common set of behavior that don’t quite look (or behave) consistently across your application, not to mention a bunch of duplicated work and copypasta code that could have been encapsulated in 1 spot. Building all of this into a compound component makes it faster and easier for developers to implement common UI features while guaranteeing that those features are consistent everywhere. That’s the whole point of having these components, right?
Building and shipping compound components lets you enforce best practices. For instance, if you’re building a component that searches a database and displays results, your component can enforce reasonable limits on data ranges and size (none of this is an excuse for not validating and enforcing this stuff on the server, but at least this way you can build it into the client too), while providing the elements needed to display and page through the results. If you have forms for user data, that could mean 1 component with all the fields (with labels) for all the parts of a street address along with logic to validate that you’re not missing any required data. If you show dashboards, it could be graph components that render both the graph and also include elements for adjusting the scope of the X and Y axes. The point is that you should include everything needed to be completely useful and to ensure that whenever someone uses it that it behaves the way you’d want it to if you were building that functionality yourself.
Shipping your own components isn’t just about controlling the look and feel of applications, it’s about controlling the user experience. That means you need to ship more than styled versions of built-in HTML elements. Each component should have everything it takes to behave exactly the way the user interface does when you code it. In fact, shipping compound components with all related user experience logic built-in speeds up your development too, since you can spend your time on user-interface elements specific to what you’re doing right then, or (if you’re a team of full-stack developers) the back-end business logic.
It’s easy to say that you should ship components with more to them than a single HTML element, but balancing “big enough that solve common problems” vs. “small enough to be a viable building block” is tough. The reality is that sizing components is probably more art than science, but it’s an art worth pursuing. Doing it means focusing on user behavior, not just on look and feel. Finding common interactions will tell you just what should go into the more complex components you’ll be shipping. In theory, these common usage patterns have likely led to some code abstraction already, so those UI elements are a good place to start. In the end, it’ll take a combination of experience, judgement, and some clever engineering to build a set of complex, but usable components representing multiple smaller components and encapsulating how they interact with each other.
It’s 1 thing to say you ship a components library, but it’s another to have that library contain components that solve problems and make life easier for developers. That takes time and a tight feedback loop. It also involves building compound components that control how these smaller, child components interact with each other. Building those compound components helps you to control the user experience even when you’re not the person doing the coding. If you want to make sure you’re shipping usable components, start small, like a simple input field and label. After that, find common groupings and patterns (like search, or graphing data, or displaying query results), and start abstracting them down until you have 1 component that handles the whole pattern for you. That’s the type of component you should be shipping.