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
What's up?
We're sooooo close to finishing Mastering Nuxt!
This week I'm working on the last set of videos for Mastering Nuxt, the module on authentication. Then, I need to re-record a few videos to fix some mistakes and tidy things up.
Once Nuxt 4 stable is released, I'll re-record the very first video (where we install Nuxt) so it's all up-to-date.
It takes a bit to edit and polish things up after recording, but we're looking good for finishing everything up not too long after Nuxt 4 drops!
In other Nuxt news, my friend, Adam DeHaven, wrote a great article on how to power multi-tenant applications with Nuxt.
Even if you don't have a multi-tenant application, this is a great article to read, filled with tons of advanced Nuxt tips and tricks.
Powering Multi-Tenant Applications with Nuxt
Now, on to your weekly tips!
— Michael
If you ever need to debug what’s happening inside of a template, you can just throw in a function:
<template><div v-for="i in 4" :key="i">{{ log(i) }}{{ i + 1 }}</div></template>
Vue will execute anything within the curly braces as Javascript, so this function is called normally.
It can be whatever you want. Set it to console.log
if you just want to log out some values:
const log = console.log;
Or add in a debugger
statement so you can step through the code one line at a time and inspect the variables more closely:
const log = (val) => {debugger;};
If we want global access to a debugging utility, we can use the globalProperties
field on our app config:
app.config.globalProperties.$log = console.log;
Now, we can use this $log
method in whatever component we want:
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 should just use provide
/inject
to pass down the relevant value or method:
import { provide } from 'vue';const someMethodInTheParent = () => {};provide('method', someMethodInTheParent)
Then, inject it into the child component:
import { inject } from 'vue';const method = inject('method');method();
In Vue 2, you can also use the instance property $parent
:
// Tight coupling like this is usually a bad ideathis.$parent.methodOnParentComponent();
This is simpler, but leads to higher coupling and will more easily break your application if you ever refactor.
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 these were 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.
You don't want to use props and events to connect up a component in those cases. Instead, you use provide
/inject
, $parent
, or $root
, to automatically connect the components and make things happen.
(This is similar to the Compound Component pattern)
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.
In many cases, we need to generate unique IDs for elements dynamically.
But we want this to be stable through SSR so we don’t get any hydration errors.
And while we’re at it, why don’t we make it a directive so we can easily add it to any element we want?
Here's a stripped-down version of this directive:
const generateID = () => Math.floor(Math.random() * 1000);const directive = {getSSRProps() {return { id: generateID() };},}
When using it with Nuxt, we need to create a plugin so we can register the custom directive:
// ~/plugins/dynamic-id.tsconst generateID = () => Math.floor(Math.random() * 1000);export default defineNuxtPlugin((nuxtApp) => {nuxtApp.vueApp.directive("id", {getSSRProps() {return { id: generateID() };},});});
In Vue 3.5+, you can also use the useId
composable instead:
<template><div :id="id" /></template><script setup>const id = useId();</script>
Normally, custom directives are ignored by Vue during SSR because they typically are there to manipulate the DOM. Since SSR only renders the initial DOM state, there’s no need to run them, so they’re skipped.
But there are some cases where we actually need the directives to be run on the server, such as with our dynamic ID directive.
That’s where getSSRProps
comes in.
It’s a special function on our directives that is only called during SSR, and the object returned from it is applied directly to the element, with each property becoming a new attribute of the element:
getSSRProps(binding, vnode) {// ...return {attribute,anotherAttribute,};}
Join Alex and Michael with special guest Sébastien Chopin, creator of Nuxt.js and CEO of NuxtLabs, to learn more about the origins and evolution of the Vue-based meta framework. Sébastien shares valuable lessons from open source, how he came to create Nuxt.js in the first place and which challenges came with it.
In the second part of the episode, they cover Sébastien's company NuxtLabs - from its inception over to taking Venture Capital and products, such as NuxtHub or Nuxt UI Pro.
Eventually, the NuxtLabs CEO also outlines how they handle sponsorships of maintainers, and what the future holds for Nuxt and NuxtLabs.
Watch on YouTube or listen on your favorite podcast platform.
Chapters:
In case you missed them:
Nuxt gives us a few different options for controlling when components are loaded.
In this article I explore the different options and how to use each.
Check it out here: Controlling When Components Are Loaded in Nuxt
It may not be as fun as shipping the next feature, but making sure our apps are rock-solid and can recover from errors is crucial.
Without good error-handling, our UX suffers and our applications can be hard to use.
In this article I explore handling client-side errors using the NuxtErrorBoundary
component.
Check it out here: Client-Side Error Handling in Nuxt
Here are some upcoming events you might be interested in. Let me know if I've missed any!
"Telling a programmer there's already a library to do X is like telling a songwriter there's already a song about love." — Pete Cordell
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.
To keep your composables — those extracted functions written using the composition API — neat and easy to read, here's a way to organize the code.
provide
and inject
defineProps
, defineEmits
, and defineExpose
(when using script setup
)refs
and reactive
variablesawait
(or Promises if you're into that sort of thing)Why this order? Because it more or less follows the order of execution of the code.
It's also based on the this linting rule.
Michael Hoffman curates a fantastic weekly newsletter with the best Vue and Nuxt links.
p.s. I also have a bunch of products/courses: