🔥 (#156) 3 year anniversary (and announcement! 🎉)

Hey!

I've got two things to share:

First, it's been three years of these weekly newsletters.

Thank you for reading!

Honestly, I'm surprised I haven't missed a single week in that time — although it's been close a couple times.

Second, today I'm happy to announce that Reusable Components is now available! You can check it out here (there's also a preview of the course).

To celebrate this big update, it will be 35% off for the seven days.

And just like I have for the last 3 years, here are your tips.

— Michael

🔥 Fine-grained Loading API in Nuxt

In Nuxt we can get detailed information on how our page is loading with the useLoadingIndicator composable:

const {
progress,
isLoading,
} = useLoadingIndicator();
console.log(`Loaded ${progress.value}%`); // 34%

It’s used internally by the <NuxtLoadingIndicator> component, and can be triggered through the page:loading:start and page:loading:end hooks (if you’re writing a plugin).

But we have lots of control over how the loading indicator operates:

const {
progress,
isLoading,
start, // Start from 0
set, // Overwrite progress
finish, // Finish and cleanup
clear // Clean up all timers and reset
} = useLoadingIndicator({
duration: 1000, // Defaults to 2000
throttle: 300, // Defaults to 200
});

We’re able to specifically set the duration, which is needed so we can calculate the progress as a percentage. The throttle value controls how quickly the progress value will update — useful if you have lots of interactions that you want to smooth out.

The difference between finish and clear is important. While clear resets all internal timers, it doesn’t reset any values.

The finish method is needed for that, and makes for more graceful UX. It sets the progress to 100, isLoading to true, and then waits half a second (500ms). After that, it will reset all values back to their initial state.

🔥 How to use callOnce in Nuxt

If you need to run a piece of code only once, there’s a Nuxt composable for that (since 3.9):

<script setup>
await callOnce(async () => {
// This will only be run one time, even with SSR
});
</script>

Using callOnce ensures that your code is only executed one time — either on the server during SSR or on the client when the user navigates to a new page.

It also has a key similar to useFetch or useAsyncData, to make sure that it can keep track of what’s been executed and what hasn’t:

<script setup>
['one', 'two', 'three'].forEach(item => {
// Run once for each item
callOnce(item, async () => {
// Do something with the item
});
});
</script>

By default Nuxt will use the file and line number to automatically generate a unique key, but this won’t work in all cases.

🔥 Reactive SVG components

SVGs can be reactive components, too.

After all, they're HTML elements just like div, span, and button.

Here's an SVG component that has a prop to change it's fill colour:

<template>
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" :fill="color" />
</svg>
</template>
<script>
export default {
name: "SVGComponent",
props: {
color: String,
},
};
</script>

I'm sure you can build some pretty wild things if you dig into different SVG elements and attributes.

Scoped slots and SVGs? Why not...

Here's a demo if you want to see this example in action.

📜 Suspense: Everything You Need to Know

I wrote this article for VueSchool.io to clear up some misconceptions I've seen around Suspense.

If you load data in your application, I think you'll find it useful.

There are even some code demos so you can code along with the article!

Check it out here: Suspense: Everything You Need to Know

đź’¬ One way street

"A good programmer is someone who always looks both ways before crossing a one-way street." — Doug Linder

đź§  Spaced-repetition: Nested Ref Properties in Templates

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.

One thing that’s a little tedious with refs is when you need to access a nested property within the template:

<template>
<div id="app">
<p v-for="el in arr">{{ el.value.text }}</p>
</div>
</template>
const arr = reactive([]);
arr.push(ref({ text: 'hello' }));
arr.push(ref({ text: 'world' }));
setTimeout(() => (arr[0].value.text = 'nothing'), 1000);

You can’t just rely on auto-unwrapping of refs, you have to explicitly access the .value and then grab the nested property from there:

ref.value.nestedProperty

In this case, using a reactive value might be preferable — if the syntax is really bothering you.



p.s. I also have four products/courses: Clean Components Toolkit, Vue Tips Collection 2, Mastering Nuxt 3, and Reusable Components