Angular Design Patterns: Feature and Presentation Components
In this post, we will cover the Feature and Presentation Component Design pattern. This pattern has been called many things such as
- Smart/Dumb Components
- Stateful/Stateless Components
- Container Components
For this post, we will use the terminology of Feature and Presentation. This pattern has been covered many times and across other frameworks other than Angular but I would just like to give my perspective on this useful pattern.
The Component
What is a Feature or Presentation Component? First let's define what a component is. A component is simply a small encapsulated piece of a user interface. Many frameworks and libraries use components including Angular, React, Ember, and Vue. Components allow us to break a large complex UI into smaller easier to understand pieces. This is similar to the way we break our code up into smaller functions. This makes our UI easier to understand and reuse in our applications.
When building large UI systems with components our application it becomes a tree-like structure. Components get used inside of other components. Most of the time these parent-child relationships need to communicate back and forth. In Angular components, we communicate via Input properties and Output events. Parent components can set a property of a child component, and a child component can emit events up to parent components.
This makes it clear where data is flowing from, but it brings up a few questions. Who should fetch data from the server? Which component is responsible for saving the data? This is where Feature and Presentation Components come in.
Feature vs. Presentation
In our web applications, we can take advantage of the Feature and Presentation Component pattern. Let's look at the example app below.
Here we have a product search page. The user can search for products with filters. The search view shows a list of product items.
Let's define the Feature component first. A Feature component is a top-level component that contains all other components in our feature. This commonly is a routed component in Angular. Our feature components are responsible for gathering data from various services for our feature to use. If our user saves data, the feature component is responsible for passing that data to our Angular
Services to save the data to our server API. Feature components are still very slim with the amount of application logic. We try to defer this logic to Services if possible. For this example, the product-search
is our Feature Component and will be composed of many Presentation components.
Where do Presentation Components fit in? As our Search Feature component grows in complexity, we can refactor our component into smaller reusable Presentation Components. Presentation Components behave like pure functions taking in the data via @Input
and emitting data via @Output
. This allows the majority of our UI not to know the underlying implementation detail of where the data came from. For example, a product-item
component takes in a @Input
of a product to display. This allows the product-item
component to have the only responsibility of rendering the product when the data is passed to it.
<app-product-item
[item]="itemData"
(select)="selectProduct($event)"
></app-product-item>
Our product-item
could also have a single @Output
to notify the feature component when a user clicks a product in the list. This is useful because we can now use the item component for different situations whether that should display a different view or save some information to our API.
Presentation components do not have to be reusable. Keeping the pure Inputs and Outputs makes it easier to debug features. Because our Feature Component is the only component communicating with Services it is easy to determine where the data is coming from and in turn, making it easier to debug.
There are downsides to this though. As the feature grows in complexity, we may have a deeply nested component structure. Since component events only bubble up one level at a time, we will have to pass up to each parent component manually. Sometimes introducing other sub feature or container components can help elevate this. At this point, it brings into question how to manage state which can be for another blog post :)
Presentation Components do take more time to create and hook up than just having large single Feature Components but in the long term can help the maintainability and reusability of our components. Many if not most Presentation Components can be abstracted into a style guide or UI library for the project. To get ideas of component design and style guide maintainability I recommend Brad Frost's fantastic book Atomic Design.