Skip to main content

Command Palette

Search for a command to run...

Building Vue Components with Composition

Published
5 min read

What is composition?

The Oxford Languages dictionary has one of its definitions as “a thing composed of various elements“. For our understanding of this idea, I think this is perfect.
This idea of composition is separate from the Composition API we are introduced to in Vue3. Even though it is separate, it has the same underlying idea.

What is the goal of composition?

The goal of composition is to build truly flexible, easily maintainable, easily extensible components. A good way to think about it is building a house using lego blocks. The idea is that you can switch out lego blocks with others or just remove some that you do not need.

Practical Case Study: Image Card Component

Recently, I worked on, and still am working on, I had to create an image card component that was used in different contexts and pages.

As a background, the application is an image resizer. The way I structured it was to be a 3-stage form. The first stage is where the images are uploaded. The second stage was where the resize dimensions for the different files are set. The third stage was where the image previews will be seen and then the processing action will happen and subsequently, download of completed image resize transformations.

The image card was needed to be shown on the 2nd stage of the 3-stage form. This is because I wanted to give the users visual feedback on the current image dimensions and what dimensions they would be setting the resized image to. Additionally, I wanted the users to have download buttons for each image as well as a delete button, if there is any need for a deletion before proceeding to the final processing stage.

It also needed to be shown on the 3rd stage because I needed a visual effect to show the users the different stages from uploading the images to processing the images to getting backend feedback of completed image transformation. In addition to this, I wanted the users to have download buttons for each image after the image transformation was completed.

Initial Image Card Component Design

This is the ultimate design goal of the image card, I was working with. I took some inspiration from how Loom designed the cards in their dashboard. See below.

The Image Card Component in a "Ready to Upload" state

And now the Loom prototype I was basing this off of.

The component code looked like this:

I had to pass down that large ImageState object as well as a prop to indicate whether the card is selected or not selected.

There are 2 optional props inside the ImageState interface. Both of those optional props optionally control if a part of the component shows up or not.

An even bigger problem arises when we need to add more sections to the card or if we want to use the same card component elsewhere but use less sections. For instance, if we don’t want to always show the header section or the icon part of the header section. In that instance, to still make sure the component is dynamic (because that’s the foundational idea of components) we would need to pass a prop, probably a boolean one, to conditionally render that section.
Therefore, the more dynamic/flexible you want the component to be, the more props you will need to pass. This is the main problem with building complex re-usable components.

Composition of Image Card Component

I got the idea of UI composition from Fernando Rojo in a talk he gave at React Universe Conf 2025 titled, “Composition Is All You Need“. In that talk, he used the example of the slack message input component which includes various different features based on different contexts. For instance, the slack input component is slightly different between, sending a DM, a group message, updating an edit and so on.

In the same vein, I had to redesign my Image Card Component to be as flexible as possible to allow for future customization without need for prop usage for additional UI updates. I wanted a situation where I can use as little as I want of the component or as much as I want, depending on the context of use.

Without further ado, here is how I structured the component.

I divided it first, into Image Preview and Image Content. Image Preview denotes the image and the Image Content refers to the section below the image. Below is an image of that division.

Image Content

Inside the Image Content section, I can have any number of mini components. For my use case here, I created the following components: ImageCardHeader, ImageCardMetadata, ImageCardStats, ImageCardFooter, ImageCardError.

All of these components can be composed inside the Image Content section.

Technical Details

One critical detail that makes composition work is <slot/>. This concept helps to nest components within components. An example is shown below, the main ImageCard component looks like:

So, the main ImageCard component has a slot that can accept any components, which means, I can add any components I want as nested inside that main component.

To see what a complete implementation of the composed ImageCard component would look like to aid understanding, I will now show how the technical implementation looks on the Process Image section.

Conclusion

Composition is the way to go for implementing fully flexible components where you do not need any section of your component to be compulsorily available. It is useful for components that are complex, reused across the same application.

I hope you learned something here today and are ready to apply this to your next project.