DEV Community

Liam Hall
Liam Hall

Posted on

What are higher order components and how to use them in Vue

What is a higher order component

Higher order components in Vue offer a convenient solution for code reusability by acting as a wrapper for specific component configurations. By using higher order components, we can eliminate the need for repetitive code and make updates in one central location, rather than multiple places. This makes it much simpler to maintain and update our codebase.

When might we call for higher level component

The most prevalent justification for using higher order components is to eliminate repetitive configurations throughout the application. If you find yourself constantly using the same component configurations, it may be an indication to extract that configuration into a higher order component, streamlining your codebase and making it easier to maintain.

Higher level components by example

Common configurations

Simple card example

To gain a deeper understanding of higher order components, let's examine a practical example. The card component is a staple in UI design and is often utilised to showcase preview information for articles and products, among other things. These cards tend to have many common structural elements. Imagine we have been provided the following designs for an article card (left) and a product card (right) from our design team:

Image description

These 2 components are very structurally similar, the only variations, are that the Article Card has a Border and Link, vs the Product card with no Boarder and a Button. Traditionally we'd probably create 2 components with similar structures

These two components share a great deal of structural similarity, with the main distinctions being that the Article Card has a Border and Link, vs the Product card with no Boarder and a Button. In the past, we may have created a Card component and passed it the relevant configurations wherever necessary:

Card
<template>
  <div class="card">
    <div class="card__image-wrapper">
      <img src="/some-image.jpg" :alt="title" />
    </div>
    <div :class="['card__details', borderMap[border]]">
      <h2 class="card__title">
        {{ title }}
      </h2>
      <p class="card__description">
        {{ description }}
      </p>
    </div>
    <div class="card__actions">
      <slot name="actions"></slot>
    </div>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

This approach poses a significant issue as it requires passing the same configuration for product and article cards throughout the application. When a change needs to be made, it must be updated in multiple locations, leading to a significant maintenance burden. This makes it difficult to keep the different instances of the cards in sync, and increases the likelihood of inconsistencies.

One alternative approach could be to create two separate components with largely identical structures:

Article Card
<template>
  <div class="card">
    <div class="card__image-wrapper">
      <img src="/some-image.jpg" :alt="title" />
    </div>
    <div class="card__details card__details--border-grey">
      <h2 class="card__title">
        {{ title }}
      </h2>
      <p class="card__description">
        {{ description }}
      </p>
    </div>
    <div class="card__actions">
      <a :href="link">Read more</a>
    </div>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode
Product Card
<template>
  <div class="card">
    <div class="card__image-wrapper">
      <img src="/some-image.jpg" :alt="title" />
    </div>
    <div class="card__details">
      <h2 class="card__title">
        {{ title }}
      </h2>
      <p class="card__description">
        {{ description }}
      </p>
    </div>
    <div class="card__actions">
      <BuyNowButton :id="id" />
    </div>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

While this approach may be functional, it poses a significant maintenance challenge as any future structural modifications must be made in multiple locations. This becomes increasingly cumbersome as more card-style components are added to the application. For example, if our hypothetical team decides to include recipe previews using a recipe card, we would need to make structural changes in three separate locations. This difficulty is further compounded when utilising utility-based classes, as it becomes challenging to ensure consistency across all instances.

We can address many of these challenges by combining both approaches, with the Product Card and Article Card components serving as higher-order components. This approach allows the initial Card component to remain unchanged, while still providing the flexibility to make updates and modifications in a more centralised and efficient manner. This helps to reduce the maintenance overhead and ensures consistency across all instances of the card components throughout the application:

Article Card
<template>
  <Card :image="image" :title="title" :description="description" border="grey">
    <template #actions>
      <a :href="link">Read more</a>
    </template>
  </Card>
</template>
Enter fullscreen mode Exit fullscreen mode
Product Card
<template>
  <Card :image="image" :title="title" :description="description" border="none">
    <template #actions>
      <BuyNowButton :id="id" />
    </template>
  </Card>
</template>
Enter fullscreen mode Exit fullscreen mode

By adopting higher-order components, we have created a centralised hub for managing our card components and significantly reduced the amount of repetitive configuration throughout the application. This results in a more maintainable and organised codebase, streamlining updates and modifications to a single location. This approach not only simplifies management but it also ensures consistency across all instances of the card components throughout the application.

Conclusion

So next time you find yourself repeating component configurations throughout your application, considering using the higher order component approach.
If you’ve found this article useful, please follow me on Medium, Dev and/ or Twitter.

Oldest comments (0)