Exclusive tips every week

Join 11,067 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

    🔥 (#106) Building a Twitter Scheduler with Markdown Support

    Hey,

    I've got a new article for you, as well as some tips.

    The article details how I built my own Twitter scheduler with Markdown support. I explain why I built it, some challenges I ran into, and share the most interesting bits of the project.

    Read it here: Building a Twitter Scheduler with Markdown Support.

    I'm still working on the next version of Clean Components. There isn't much to share this week —  I'm just wrapping up the planning stage and starting to work on the content.

    Enjoy your day, enjoy your week, and enjoy these tips!

    — Michael

    🔥 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.

    🔥 Using two script blocks

    The <script setup> sugar in Vue 3 is a really nice feature, but did you know you can use it and a regular <script> block?

    <script setup>
      // Composition API
      import { ref } from 'vue';
      console.log('Setting up new component instance');
      const count = ref(0);
    </script>
    
    <script>
      // ...and the options API too!
      export default {
        name: 'DoubleScript',
      };
    </script>

    This works because the <script setup> block is compiled into the component's setup() function.

    There are a few reasons why you might want to do this:

    • Use the options API — not everything has an equivalent in the composition API, like inheritAttrs.
    • Run setup code one time — because setup() is run for every component, if you have code that should only be executed once, you can't include it in <script setup>. You can put it inside the regular <script> block, though.
    • Named exports — sometimes, it's nice to export multiple things from one file, but you can only do that with the regular <script> block.

    Check out the docs for more info

    📜 Nuxt 3: Pages vs. Layouts vs. Components

    Nuxt 3 comes with 3 different ways to organize your components: pages, layouts, and components.

    It can be difficult to know which to use when, so I wrote this article to help explain the differences.

    Check it out here: Nuxt 3: Pages vs. Layouts vs. Components

    💬 Solve the Problem

    "First, solve the problem. Then, write the code." — John Johnson

    📅 Events

    May 12 and 15 — VueJS Live (Online + London)

    I'll be headed to London to give a talk at this conference.

    You won't want to miss it!

    Get your ticket here: https://vuejslive.com/

    May 24-26 — VueConf US (New Orleans)

    This year VueConf US is in New Orleans!

    The speaker lineup looks amazing, so make sure to check it out: https://us.vuejs.org/

    🧠 Spaced-repetition: Nuxt's Powerful Built-In Storage

    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.

    Nitro, the server that Nuxt 3 uses, comes with a very powerful key-value storage system:

    const storage = useStorage();
    
    // Save a value
    await storage.setItem('some:key', value);
    
    // Retrieve a value
    const item = await storage.getItem('some:key');

    It’s not a replacement for a robust database, but it’s perfect for temporary data or a caching layer.

    One great application of this “session storage” is using it during an OAuth flow.

    In the first step of the flow, we receive a state and a codeVerifier. In the second step, we receive a code along with the state again, which let’s us use the codeVerifier to verify that the code is authentic.

    We need to store the codeVerifier in between these steps, but only for a few minutes — perfect for Nitro’s storage!

    The first step in the /oauth endpoint we store the codeVerifier:

    // ~/server/api/oauth
    
    // ...
    const storage = useStorage();
    const key = `verifier:${state}`;
    await storage.setItem(key, codeVerifier);
    // ...

    Then we retrieve it during the second step in the /callback endpoint:

    // ~/server/api/callback
    
    // ...
    const storage = useStorage();
    const key = `verifier:${state}`;
    const codeVerifier = await storage.getItem(key);
    // ...

    A simple and easy solution, with no need to add a new table to our database and deal with an extra migration.

    This just scratches the surface. Learn more about the unstorage package that powers this: https://github.com/unjs/unstorage



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

    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
    🎉 Get 30% off Vue Tips Collection!
    Get it now!