DEV Community

Cover image for The Perfect Wrapper Components in Vue 2.6 and soon Vue 3.0
Vaibhav Namburi
Vaibhav Namburi

Posted on • Updated on • Originally published at

The Perfect Wrapper Components in Vue 2.6 and soon Vue 3.0

Do you want to build a codebase that looks like a teenagers bedroom or do you want to build one that looks like its straight out of IKEA.

The difference in clean, predictable and quick code and code that is inconsistent, buggy and takes centuries to add features to boils into a couple of components.

The component we’re talking about today comes in the form of design system components, mainly wrapper components. As a Vue / Frontend developer — you NEED to work with your designer buddy to make sure the design system being built is concise and “inheritable”.

Inheritable? Yeah — inheritable, i.e design pattern that builds on top of itself, evolving design, rather that components that look like they belong to different projects — this is exactly how we built

Alright — enough fluff, how do we build a strong wrapper component structure? Let’s use in our example an input field and a button.

Step 1:

Solution architecture questions:

  • What states does the input field have ?
  • Where is data being fetched and sent
  • Will it be maintaining it’s own data layer or absorbing from a parent
  • Do I directly sync all my input fields straight to a central management store like Vuex ?

Step 2:

  • Hover, OnFocus, Error
  • Parent component
  • No it won’t, data layer is from the parent (in this instance, you can do it your way)
  • No, we’re syncing all data to the parent of the input Great now thats done, lets look at a very simple input component.
  • We have a root label wrapping the input component
  • We bind the input component to $attrs (more on this very soon)
  • We listen to “$listeners” (whatever that is)
  • Lastly we listen to on “input” changes and emit “change” event to the parent

VueJS knew that wrapper components were idealistic and natural of every component driven framework. A part of the “prop” proposition is that attributes or “directives” passed down from the parent not recognised as props by the child automatically get attached to the root of the child…confusing

Imagine passing greeting into the input wrapper, if greeting wasn’t registered as a prop — it would be added as a field onto the wrapper of our input field, by setting inheritAttrs to false we prevent that, and instead override that and pass all the meta data directly into the input component.

This way you don’t need to register EVERY prop in the input wrapper, but essentially it passes itself through the wrapper naturally (as a wrapper should allow)

Secondly we have this thing called $listeners — this is Vue’s great way of basically bubbling all events the component listens to up to the parent, this way you don’t need to register each event manually — again, a great way to create a wrapper component.

The goal of the wrapper is to essentially provide some design conformity — PS wrappers should 100% be responsible for custom logic as well, you can easily add as much custom logic to a wrapper component as needed, you can get access to the value of the input field by registering value as a prop.

What we’re left with is an input wrapper with the model defined in the scope of the component where the wrapper is created, and attributes that are meant to be passed directly to the input component will be register as expected.

This is exactly how the entire Flowyse project is built out, and one of the reasons why I could ship it out in 5.5 weeks.


With Vue 3.0 you don’t need so much config when creating your base wrapper component

Our component gets MUCH more simpler to use

Notice we no longer have $listeners or inheritAttrs

In Vue 3.0 there’s no more automatic inheritance of attributes, which means $attrs automatically includes all non-prop related attributes without needing to define inheritAttrs: false .

Even with the listeners v-on compiles straight to the attributes @enter compiles to on-enter . By simply doing v-bind="attrs".

"attrs" include all non-emitted listeners as well, and the best part is….

v-model compiles into model-value and on-model-update by doing v-bind="$attrs" so no more model option nor do we need to override the native input event like we did before

Top comments (5)

bootcode profile image
Robin Palotai

Could you provide an example on the v-on usage? I have limited Vue experience, and don't understand the situation how this would be used. Thanks!

Re IKEA code:

  • With each component you buy, you get notepad.exe attached on a floppy. Now you have a box full of notepad.exe floppies.

  • The source code came with just one less parenthesis, so you need to go to the local symbol shop to finish the code.

  • else branches start to get loose the second time you refactor the code, and break off the third time.

jackedwardlyons profile image
Jack Lyons

Great post! Please share more tips and examples!!

veebuv profile image
Vaibhav Namburi

Thanks so much Jack! more to come for sure!

denisinvader profile image
Mikhail Panichev

We use this strategy for about a year yet. Chris Fritz told about this pattern (transparent wrappers) more than one year ago and this is not related to Vue 3 at all

veebuv profile image
Vaibhav Namburi

Awesome that you use this pattern for a year, we do to 🙂- fantastic RE Chris Fritz video - I just saw all of it, it was excellent, thanks for sharing!

If you had the chance to scroll to the bottom, you'll be able to see the update Evan You announced that would be available in 2.6 & 3.0

PS this is my original article on medium - just cross-posting here : )