🔥 Vue Tips #28: Easily create component variations with the Base Component pattern

Michael Thiessen - Oct 5 '21 - - Dev Community

This newsletter was sent out to my list on September 29, 2021. Sign up here to get emails like this each week!

What's up?

You may have noticed that recently I've been experimenting with adding sponsors to this newsletter.

Honestly, I'm not sure I like putting them in here.

It's extra work that I don't particularly enjoy, and I have the feeling it doesn't make the newlsetter better for you either. Of course, if it made boatloads of cash so I could focus more time on this then it would be worth it, but that's just not the case...

I'd rather spend that time on writing better tips for you.

— Michael

🔥 Directly accessing parent components (and why)

Props down, events up. That's how your components should communicate — most of the time.

But in rare cases, that just doesn't work.

If you need direct access to the parent component, you can use the instance property $parent:

// Tight coupling like this is usually a bad idea
this.$parent.methodOnParentComponent();
Enter fullscreen mode Exit fullscreen mode

You can also get direct access to the application root, the very top-most component in the tree, by using $root. Vue 2 also has $children, but it was taken out for Vue 3 (please don't use this one).

When would these be useful?

There are a few different scenarios I can think of. Usually when you want to abstract some behaviour and have it work "magically" behind the scenes.

In those cases, you don't want to use props and events to connect up a component. Rather, you use provide/inject, $parent, or $root, to automatically connect the components and make things happen.

But it's hard to come up with an example where this is the best solution. Using provide/inject is almost always the better choice.

🔥 Easily create component variations with the Base Component pattern

The Base Component pattern is one of my favourite ways to make lots of different versions and variants from a single component.

It has a few basic steps:

  1. Create your base component
  2. Wrap it with another component to get a variant of the original
  3. Repeat step 2 as many times as you need

Here's an example, creating a DisabledButton variant out of a BaseButton component:

<!-- DisabledButton.vue -->
<template>
  <!-- Never forget how to create this disabled button.
       Package it up using the Base Component pattern. -->
  <BaseButton
    type="disabled"
    disabled
  >
    <!-- You can't accidentally use the wrong icon now.
         It's provided here for you -->
    <template #icon>
      <Icon type="disabled" />
    </template>
  </BaseButton>
</template>
Enter fullscreen mode Exit fullscreen mode

You can use this pattern in many different ways:

  • Lock down props — take a Button component and hard code a few props to get a DisabledButton. Now you can just use the DisabledButton directly without having to fiddle with all the necessary props each time.
  • Lock down slots — create an InfoButton variant where the icon passed to the Button is always the same. Now, if you ever need to change the icon (or anything else) you can do it in one place.
  • Simplify props — sometimes components end up with dozens of props, most of which are for edge cases. Create a BaseButton with all the props, and a Button that passes on only the most common ones. This is a lot safer, easier to use, and the documentation is easier to read too.

I've included more on this pattern in Reusable Components.

đź“ś How do you structure a Vue project?

Just an hour ago, Marco asked me this very question.

My quick response was: keep it flat and simple, and when things start to get messy, slowly add in folders.

An even better response: Markus wrote a great article on this, and he goes into much more detail and provides some more specific advice.

Read his article here: Vue Project Directory Structure

đź’¬ Software Quote

Automating chaos just gives faster chaos. — Mark Fewster

This is the paradox of abstraction:

Creating a component and reusing it over and over can save you a lot of time.

But if that component is wrong or has a bug, you also multiply that problem over and over 🤦‍♂️

🧠 Spaced-repetition: Aria roles you didn't know you needed

The best way to commit something to long-term memory is to periodically review it, gradually increasing the time between reviews 👨‍🔬

Actually remembering these tips is much more useful than just a quick distraction, so here's a tip from a couple weeks ago to jog your memory.

Aria roles are used to tell a screenreader what an element is for.

This is really important when the native HTML elemen just doesn't exist (eg. roles like toolbar and alert) or when you're using a different HTML element for design or technical reasons (eg. wrapping a radio button to style it).

But please, remember that you should always use the semantic element where you can. This is always the best and most effective solution.

There are six different categories of aria roles:

  1. Widget - roles like button, checkbox, separator, tab, or scrollbar
  2. Composite - roles like combobox and listbox (these are for dropdown menus), radiogroup, or tree
  3. Document structure - this includes article, presentation, figure, feed, and directory
  4. Landmark - banner, main, navigation, and region are roles in this category
  5. Live region - alert, log, marquee, and status are roles that might update with real-time information
  6. Window - alertdialog and dialog are the only two roles in this category

You can check out the full list here: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques#roles

Exclusive tips and insights every week

Join 8135 other Vue devs and get exclusive tips and insights like these delivered straight to your inbox, every week.

You have great content in your emails. I seriously learn something from every one of them. — Titus Decali

Thanks for another beautiful tip 🙏 — Victor Onuoha

Loving these, and the spaced repetition — Mark Goldstein

Sign up here

. . . . . . . . . . . . . . .