Exclusive tips every week

Join 13,567+ other Vue devs and get exclusive tips and insights delivered straight to your inbox, every week.

    Picture of Michael Thiessen

    đź‘‹Hey friend! I work hard to send you amazing stuff each week.

    — Michael

    I really love and enjoy reading these emails.

    You are one of the most pro VueJS devs I know, and I am happy that you share this knowledge.

    Fabian Beer

    Here's my latest newsletter

    🔥 (248) Lonely Children, Debugging Templates, and Lightweight State Management

    Hey!

    Can't believe we're already in the middle of December. Hope you're getting everything wrapped up before the holidays and finding some time to relax.

    Here's some Vue and Nuxt content to enjoy with your coffee.

    — Michael

    🔥 Lonely children

    Here's a technique that makes it super easy to simplify nested elements:

    You take everything inside a v-if or v-for and extract it into a new component.

    You'll go from this, with some nesting:

    <template>
    <div>
    <!-- ... -->
    <div v-for="item in list">
    <h2 class="item-title">
    {{ item.title }}
    </h2>
    <p class="item-description">
    {{ item.description }}
    </p>
    </div>
    <!-- ... -->
    </div>
    </template>

    To this, where the nesting is gone:

    <template>
    <div>
    <!-- ... -->
    <ListItem
    v-for="item in list"
    :item="item"
    />
    <!-- ... -->
    </div>
    </template>

    To do this, you extract the code in the v-for into a new component:

    <!-- ListItem.vue -->
    <template>
    <div>
    <h2 class="item-title">
    {{ item.title }}
    </h2>
    <p class="item-description">
    {{ item.description }}
    </p>
    </div>
    </template>

    This technique becomes more and more valuable the more nesting you have.

    Note: You can choose to do this recursively, taking every v-for or v-if and creating a new component. But often, it's simpler to grab a more significant chunk of the template and remove most of the nesting with one new component.

    I've written about this technique in more detail here.

    🔥 Debugging Templates

    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:

    🔥 Lightweight State Management

    We can add computed and methods directly on to a reactive object:

    const counter = reactive({
    count: 0,
    increment() {
    this.count += 1;
    },
    decrement() {
    this.count -= 1;
    },
    });

    This works because this is set to the object that the method is accessed through, which happens to be the reactive object.

    Vue’s reactivity system uses Proxies to watch for when a property is accessed and updated. In this case, we have a small overhead from accessing the method as a property on the object, but it doesn’t trigger any updates.

    If we had a whole series of counters we can reuse this over and over:

    const listOfCounters = [];
    for (const i = 0; i < 10; i++) {
    const counter = reactive({
    id: i,
    count: 0,
    increment() {
    this.count += 1;
    },
    decrement() {
    this.count -= 1;
    },
    })
    listOfCounters.push(counter);
    }

    In our template we can use the counters individually:

    <div v-for="counter in listOfCounters" :key="counter.id">
    <button @click="counter.decrement()">-</button>
    {{ counter.count }}
    <button @click="counter.increment()">+</button>
    </div>

    Instead of making the entire object reactive, we can use ref to make only our state reactive:

    const counter = {
    count: ref(0),
    increment() {
    this.count.value += 1;
    },
    decrement() {
    this.count.value -= 1;
    },
    };

    This saves us a small and likely unnoticeable overhead. But it also feels somewhat better since we’re being more thoughtful with our use of reactivity instead of spraying it everywhere.

    Here’s our example from before, but this time I’m going to add in a factory function to make it more readable:

    const createCounter = (i) => ({
    id: i,
    count: ref(0),
    increment() {
    this.count.value += 1;
    },
    decrement() {
    this.count.value -= 1;
    },
    });
    const listOfCounters = [];
    for (const i = 0; i < 10; i++) {
    listOfCounters.push(createCounter(i));
    }

    Of course, we can use a factory method with the previous reactive method as well.

    📜 Create Beautiful PDFs with HTML, CSS, and Markdown

    I wasted thousands of dollars hiring someone to format and layout a previous book, but it was a painful process.

    Then I built an easy-to-use tool that lets me use just HTML, CSS, and Markdown to create beautiful ebooks and PDFs.

    In this article, I share exactly how I do it.

    Check it out here: Create Beautiful PDFs with HTML, CSS, and Markdown

    📜 Compressing Images with Vite and VSharp

    Images are one of the biggest causes of slow webpages.

    We have a bunch of strategies for dealing with this, but the most basic one is to make sure each and every image in your app is compressed as much as possible.

    Fortunately for us, setting up Nuxt to automatically compress our images only takes a few minutes.

    Check it out here: Compressing Images with Vite and VSharp

    đź’¬ Bugs

    "If debugging is the process of removing software bugs, then programming must be the process of putting them in." — Edsger Dijkstra

    đź§  Spaced-repetition: Start with the Interface

    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.

    When writing a composable, don’t immediately dive into implementing it.

    Instead, take a moment to figure out how you will be using the component. Take some time to think about the interface between the composable and the rest of your app.

    A few minutes upfront can save you a lot of tears and frustration later on.

    Here are a few questions you may want to ask yourself before starting:

    1. What arguments should the composable receive?
    2. What options do we want to include?
    3. What does the composable return?
    4. Do we want to use dynamic return values here?
    5. What does the minimum useful version look like, and how quickly can we get there?
    6. What does the final version look like? Is there anything easy we can do now to prepare for that?

    Of course, your composable will change and evolve over time.

    But it’s much easier to start off heading in the right direction.

    Michael Hoffman curates a fantastic weekly newsletter with the best Vue and Nuxt links.

    Sign up for it here.

    p.s. I also have a bunch of products/courses:

    Here's what others are saying

    I'm starting to think that your newsletter is one of the best things that happened to me this year. I just love these emails.
    Stanislaw Gregor
    I'm somewhere in the upper-middle level at Vue, and I never miss an email you and always find something cool when reading!
    Eduard Climov
    This is the first time where I'm actually enjoying email newsletters. I like your format a lot.
    Fahmi Alfituri
    You have great content in your emails. I seriously learn something from every one of them.
    Titus Decali
    Just wanted to say I enjoy these emails. They encourage me to constantly improve my craft. Fantastic work.
    Joe Radman
    Thanks for another beautiful tip 🙏
    Victor Martins Onuoha
    Loving these, and the spaced repetition.
    Mark Goldstein
    I really enjoy reading your emails, because I love Vue. Thanks for these emails.
    Arturo Espinoza
    I really love and enjoy reading these emails. You are one of the most pro VueJS devs I know, and I am happy that you share this knowledge.
    Fabian Beer
    THANK YOU! I did not know about the deep property, so I assumed you simply couldn't watch objects.
    Darryl Noakes
    I really must say you are doing a really great job. Now I am aware of a cleaner and more performant way to use Tailwind. Thanks a lot!
    Henry Eze
    Thank you so much, I really enjoy and appreciate your emails.
    Carlos Gonzalez
    Thanks for sharing great Vue tips.
    Fernando Navarro
    I really enjoy these tips.
    Martin H
    Thank you so much for the weekly Vue education. Thanks and live on longer to educate us more.
    Kabolobari Benakole
    I look forward to your emails every week. This week was something I knew, but I like to be reminded of. Thanks for keeping it up!
    Nathan Strutz
    Thank you for your weekly emails. I always look forward to learning a new tip about Vue or at least relearning old tips :)
    Colin Johnson
    I have really been enjoying your weekly emails, and I also got my two team members to join in on the fun as well.
    Keith Dawson
    Thank you for your newsletter, your explanations have very concise examples and I like it.
    Nicolas Decayeux
    Thanks A LOT for your great work!!! One of the few newsletters that I let pass!
    Martin Tillmann

    Want to level up your Vue skills?

    With over two million reads and 11,067 subscribers, you've come to the right place.

    Subscribe now to get exclusive insights and tips every week.