Join 13,567+ other Vue devs and get exclusive tips and insights delivered straight to your inbox, every week.
đź‘‹Hey friend! I work hard to send you amazing stuff each week.
— Michael
Hi!
This week is a bit quieter for me. I just wrapped up the launch of Advanced Reactivity, so I'm switching gears a little.
So, no new articles for you this week, but I've got some from the archives that you might have missed!
And of course, your tips, as always.
Have a fantastic week!
— Michael
Computed refs are cool and all, but did you know you can create writable computed refs?
const firstName = ref('');const lastName = ref('');const fullName = computed({get: () => `${firstName.value} ${lastName.value}`,set: (val) => {const split = val.split(' '); // ['Michael', 'Thiessen']firstName.value = split[0]; // 'Michael'lastName.value = split[1]; // 'Thiessen'}});fullName.value = 'Michael Thiessen';console.log(lastName.value); // 'Thiessen'
If you want to learn more about writable computed refs (and other advanced reactivity patterns), check out my course Advanced Reactivity.
It’s just as important to know when not to use a pattern as it is to know how to use a pattern.
For Hidden Components, it comes down to whether we’re dealing with dynamic, reactive values, or values that are hard-coded.
Following this pattern we factor out the collapse
prop. We end up with two components, for when collapse
is true
and for when it's false
:
<!-- `collapse` is true --><ArticlesCollapsed /><!-- `collapse` is true --><ArticlesExpanded />
But if we always use these components where we're dynamically switching between them, we now have to use an extra wrapper component:
<!-- Articles.vue --><template><ArticlesCollapsed v-if="collapse" /><ArticlesExpanded v-else /></template>
Our code is easier to understand, but we haven't necessarily simplified our code.
In a way, we’re back to where we started, switching between behaviours based on the collapse
prop.
This is because if we always use collapse
dynamically, then the collapsed and expanded versions aren't really separate components anymore. They're two distinct ways of using the same component.
So in this case, if we’re only using collapse
dynamically, we don’t gain much by having separated these components. Yes, it is more organized, but not much simpler.
But we may mix dynamic and hard-coded usage of this component throughout our app.
This makes our wrapper component quite useful, since we now have three options to choose from now. Each one very clearly shows the intended usage, making our code easy to understand:
<ArticlesCollapsed /><ArticlesExpanded /><Articles :collapse="isCollapsed" />
To sum up:
ArticlesCollapsed
and ArticlesExpanded
Learn more about this pattern in the Clean Components Toolkit.
Render functions provide a powerful and flexible way to programmatically define the output of components.
This approach is particularly useful when you need more control over the rendering process than templates offer.
Consider a scenario where you're building a custom If...Else
component that conditionally renders content based on a given condition (we're just doing this for fun, okay?)
Here's a simplified example of how you might use a render function in an If
component:
export default {props: ['val'],setup(props, { slots }) {return () => {const children = [];if (props.val && slots.true) {children.push(slots.true());} else if (!props.val && slots.false) {children.push(slots.false());}return children;};},};
In this example, the If
component takes a val
prop to determine which slot to render — true
or false
. The render function checks props.val
and dynamically decides which slot to render based on its value.
Render functions are especially useful when:
While render functions offer great power and flexibility, they come at the cost of reduced readability and simplicity compared to template syntax.
So it's generally recommended to use them only when necessary, keeping the balance between functionality and maintainability.
Learn how to access DOM elements with the useTemplateRef composable.
I went deep into the useTemplateRef composable, and how it compares to the old way of accessing DOM elements.
I also share some best practices for using this composable!
Check it out here: How to access DOM elements with useTemplateRef
Trying to manage database schemas alongside your Nuxt app types can be a challenge.
But with Prisma, most of these problems go away.
It handles all of the boilerplate and coordination, so you just write one single schema that’s used in your database and in your TypeScript app.
In this article I show you how to create your Prisma schema.
Check it out here: Prisma with Nuxt: Creating the Prisma Schema (2 of 5)
"The code we release will be imperfect. Not buggy. Imperfect. There’s a difference. We know as a fact that some of what our code does will be wrong; we just don’t know exactly what will be wrong. That’s tech debt." — Allen Holub
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.
Here's how you can create web components in Vue.
First, create the custom element from a Vue component using defineCustomElement
:
import { defineCustomElement } from 'vue';import MyVueComponent from './MyVueComponent.vue';const customElement = defineCustomElement(MyVueComponent);
Second, register the custom element with the DOM:
customElements.define('my-vue-component', customElement);
Third, use the custom element in your HTML:
<html><head></head><body><my-vue-component></my-vue-component></body></html>
Now you've got a custom web component that doesn't need a framework and can run natively in the browser!
Check out the docs for more details on how this works.
Michael Hoffman curates a fantastic weekly newsletter with the best Vue and Nuxt links.
p.s. I also have a bunch of products/courses: