Vue.js Performance Guide

Generally, we as devs know that nobody has time to wait for our app to load. People are more likely to just leave as page load time goes up.

We don’t want to build slow apps, but there are many reasons why it happens anyway. Maybe there was no time or budget for „invisible“ things like performance. Maybe other technical challenges needed a lot of attention, and you had pressure to ship. Or your app just grew a lot over time!

Facing performance issues often makes developers feel guilty that they didn’t „build it better“, and worried that they don’t know enough to solve the issues. If you feel this way, let me tell you that there are a lot of quick wins and methods available to start optimizing.

In this article, we’re taking a look at the basics of performance optimization for Vue apps and guide you towards resources and tools that help you go deeper.

Why is performance optimization important?

😊 It makes users happy

Performance has a big impact on user experience. People are using your app to get things done. A snappy app that delivers results and information fast will help users accomplish their goals while feeling efficient. It also boosts trust in your brand and app.

While I hope you like making people happy, contented users are also important for your business:

🔥 It makes your app more successful

Happy users that don’t have to wait for your app or website to load are *less likely to bounce (= leave), and more likely to convert (= sign up and pay).

Additionally, performance impacts your ranking on search in two ways:

  1. Search engines rank performant pages higher
  2. If fewer people bounce quickly from your page, search engines take that as a signal that your content is helpful - therefore ranking you higher

🦾 It makes your app more accessible

Not everyone is using your app / website on a brand-new MacBook Pro and on a flat-rate, high-speed network. Being mindful about size and CPU impact makes it possible and less costly for more people to access your app.

7 factors that impact Vue app performance, and how to optimize them

When we’re talking about performance, we’re usually talking about metrics measuring two important aspects:

  • Load time: How fast does the page and content load? How long does it take until it’s interactive?
  • Responsiveness: How fast and smooth does the app respond when I interact with it? (Menus, buttons, forms, data tables, charts, filters, animations,..)

So, what are we doing (or not doing) to make our app heavier to load and less responsive? There are many things that can impact performance, and it’s not just about the code you write as a frontend developer.

You’ll have to see the broader picture including the user and their hardware & network, your backend, server and also the design of your app (visual design and architecture-wise).

Read on for a summary of the most important factors and some tips on how to optimize!

1️⃣ Bundle size

How much bandwidth is needed to load your app? The larger, the slower.

  • Keep an eye on your bundle size and reduce third party code. Choose your dependencies carefully and weigh up the benefit of the functionality against the impact of bundle size.
  • Remove unused code. Use a build step with a code bundler like Webpack or Vite (if possible) to make use of tree-shaking. This allows you to get rid of any code you don’t actually use (e.g. some of Vue’s APIs or parts of your UI component library).
  • Use lazy loading and code splitting. Split your bundle into smaller parts that are loaded only when needed to avoid loading unused parts of your app.
  • Compress / minify your files.
            <div class="brick__image-inner "
                 id="js-image-2350"
                 v-lazy-bg="'https://madewithnetworkfra.fra1.digitaloceanspaces.com/spatie-space-production/26363/vite-2.jpg'"
                style=" ">

            </div>



    </div>

    <div class="brick__caption">
                    <div class="brick__caption-upper">
            <a  href="https://madewithvuejs.com/vite" class="brick__title">
                Vite Plugin Vue
            </a>
            <span class="brick__tagline">
            Frontend Build Tool
        </span>
        </div>
        <div class="brick__caption-lower">
            <div class="pull-left">

                                        <a href="/dev-tools">#Dev Tools</a>
                                                        </div>
            <div class="pull-right">
        <div class="brick__views">
        <svg class="svg-inline u__va--middle" width="20px" height="13px" viewBox="0 0 20 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>icon-eye-dark</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
    <g id="Icons" transform="translate(-313.000000, -230.000000)" fill-rule="nonzero" fill="#435B71">
        <g id="icon-eye-dark" transform="translate(313.000000, 230.000000)">
            <path d="M9.57827924,0.0932647368 C5.40166592,0.0977121053 2.23909537,3.28527789 0.162366374,6.09013053 C-0.0164829778,6.33193046 -0.0164829778,6.66216481 0.162366374,6.90396474 C2.23923803,9.71401737 5.40085034,12.9011726 9.57827924,12.9068174 C9.57872397,12.9068178 9.57916871,12.9068178 9.57961345,12.9068174 C13.7562192,12.90237 16.9187977,9.71477 18.9955264,6.90995158 C19.1743758,6.66815165 19.1743758,6.3379173 18.9955264,6.09611737 C16.9186536,3.28603053 13.7570499,0.0989094737 9.57961345,0.0932647368 C9.57916871,0.0932643033 9.57872397,0.0932643033 9.57827924,0.0932647368 L9.57827924,0.0932647368 Z M9.57827924,1.46168579 L9.57961345,1.46168579 C12.8675916,1.46681737 15.5988249,3.96846211 17.5569468,6.50236737 C15.5985806,9.03230421 12.8660264,11.5341884 9.57961345,11.5383963 C6.29104758,11.5339489 3.55933568,9.03192789 1.60094367,6.49771474 C3.55930832,3.96777789 6.29185563,1.46589368 9.57827718,1.46168579 L9.57827924,1.46168579 Z" id="Shape"></path>
            <path d="M9.57894737,3.42075158 C7.88659608,3.42075158 6.5,4.80737263 6.5,6.49969895 C6.5,8.19205947 7.88659608,9.57864632 9.57894737,9.57864632 C11.2712987,9.57864632 12.6578947,8.19205947 12.6578947,6.49969895 C12.6578947,4.80737263 11.2712987,3.42075158 9.57894737,3.42075158 Z M9.57894737,4.78917263 C10.5317492,4.78917263 11.2894737,5.54690158 11.2894737,6.49969895 C11.2894737,7.45249632 10.5317492,8.21022526 9.57894737,8.21022526 C8.62614555,8.21022526 7.86842105,7.45249632 7.86842105,6.49969895 C7.86842105,5.54690158 8.62614555,4.78917263 9.57894737,4.78917263 Z" id="Shape"></path>
        </g>
    </g>
</g>
0
        </div>

2️⃣ Asset handling

How much bandwidth is needed specifically to load images, videos and fonts? Are you smart about delivering only what is needed?

  • Optimize your images. Make sure your images are properly sized and compressed, and serve responsive images. Think about using next-gen formats like WebP for better compression, and serve animated content in video formats where possible. Define the correct dimensions on image elements.
  • Optimize font usage. Only load the fonts you need, and make sure text is visible for users until they are finished loading (font-display: swap;).
  • Question design choices. It’s not exactly coding, but it’s still your responsibility to point out implications of design decisions on performance! Do you really need 6 different styles of your fonts from light to extra bold or can you reduce together with the designer? Do you need this large video background for the header? Sometimes the answer will be yes, and that’s okay – but maybe you can use an image fallback for slower connections?
  • Lazy-load images and videos. It makes sense to only load the resources that your users need at that moment. Postpone loading images and videos that are off-screen at first, and lazy load them later.
  • Preload / prefetch important assets. Prioritize the fetching of critical assets – for example by preloading images in your header or hero section so users see them faster.
  • Use caching so your users don't have to download the same assets multiple times.
            <div class="brick__image-inner "
                 id="js-image-3750"
                 v-lazy-bg="'https://madewithnetworkfra.fra1.digitaloceanspaces.com/spatie-space-production/29018/vue-lazyload.jpg'"
                style=" ">

            </div>



    </div>

    <div class="brick__caption">
                    <div class="brick__caption-upper">
            <a  href="https://madewithvuejs.com/vue-lazyload" class="brick__title">
                Vue Lazyload
            </a>
            <span class="brick__tagline">
            Lazyload Images &amp; Components
        </span>
        </div>
        <div class="brick__caption-lower">
            <div class="pull-left">

                                        <a href="/ui-components">#UI Components</a>
                                                                <a href="/image">#Image</a>
                                                                <a href="/utilities">#Utilities</a>
                                                <span class="dots">...</span>
                                                        </div>
            <div class="pull-right">
        <div class="brick__views">
        <svg class="svg-inline u__va--middle" width="20px" height="13px" viewBox="0 0 20 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>icon-eye-dark</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
    <g id="Icons" transform="translate(-313.000000, -230.000000)" fill-rule="nonzero" fill="#435B71">
        <g id="icon-eye-dark" transform="translate(313.000000, 230.000000)">
            <path d="M9.57827924,0.0932647368 C5.40166592,0.0977121053 2.23909537,3.28527789 0.162366374,6.09013053 C-0.0164829778,6.33193046 -0.0164829778,6.66216481 0.162366374,6.90396474 C2.23923803,9.71401737 5.40085034,12.9011726 9.57827924,12.9068174 C9.57872397,12.9068178 9.57916871,12.9068178 9.57961345,12.9068174 C13.7562192,12.90237 16.9187977,9.71477 18.9955264,6.90995158 C19.1743758,6.66815165 19.1743758,6.3379173 18.9955264,6.09611737 C16.9186536,3.28603053 13.7570499,0.0989094737 9.57961345,0.0932647368 C9.57916871,0.0932643033 9.57872397,0.0932643033 9.57827924,0.0932647368 L9.57827924,0.0932647368 Z M9.57827924,1.46168579 L9.57961345,1.46168579 C12.8675916,1.46681737 15.5988249,3.96846211 17.5569468,6.50236737 C15.5985806,9.03230421 12.8660264,11.5341884 9.57961345,11.5383963 C6.29104758,11.5339489 3.55933568,9.03192789 1.60094367,6.49771474 C3.55930832,3.96777789 6.29185563,1.46589368 9.57827718,1.46168579 L9.57827924,1.46168579 Z" id="Shape"></path>
            <path d="M9.57894737,3.42075158 C7.88659608,3.42075158 6.5,4.80737263 6.5,6.49969895 C6.5,8.19205947 7.88659608,9.57864632 9.57894737,9.57864632 C11.2712987,9.57864632 12.6578947,8.19205947 12.6578947,6.49969895 C12.6578947,4.80737263 11.2712987,3.42075158 9.57894737,3.42075158 Z M9.57894737,4.78917263 C10.5317492,4.78917263 11.2894737,5.54690158 11.2894737,6.49969895 C11.2894737,7.45249632 10.5317492,8.21022526 9.57894737,8.21022526 C8.62614555,8.21022526 7.86842105,7.45249632 7.86842105,6.49969895 C7.86842105,5.54690158 8.62614555,4.78917263 9.57894737,4.78917263 Z" id="Shape"></path>
        </g>
    </g>
</g>
0
        </div>
            <div class="brick__image-inner "
                 id="js-image-3065"
                 v-lazy-bg="'https://madewithnetworkfra.fra1.digitaloceanspaces.com/spatie-space-production/31493/nuxt-image-3.jpg'"
                style=" ">

            </div>



    </div>

    <div class="brick__caption">
                    <div class="brick__caption-upper">
            <a  href="https://madewithvuejs.com/nuxt-image" class="brick__title">
                Nuxt Image
            </a>
            <span class="brick__tagline">
            Optimized Images for Nuxt
        </span>
        </div>
        <div class="brick__caption-lower">
            <div class="pull-left">

                                        <a href="/image">#Image</a>
                                                                <a href="/utilities">#Utilities</a>
                                                        </div>
            <div class="pull-right">
        <div class="brick__views">
        <svg class="svg-inline u__va--middle" width="20px" height="13px" viewBox="0 0 20 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>icon-eye-dark</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
    <g id="Icons" transform="translate(-313.000000, -230.000000)" fill-rule="nonzero" fill="#435B71">
        <g id="icon-eye-dark" transform="translate(313.000000, 230.000000)">
            <path d="M9.57827924,0.0932647368 C5.40166592,0.0977121053 2.23909537,3.28527789 0.162366374,6.09013053 C-0.0164829778,6.33193046 -0.0164829778,6.66216481 0.162366374,6.90396474 C2.23923803,9.71401737 5.40085034,12.9011726 9.57827924,12.9068174 C9.57872397,12.9068178 9.57916871,12.9068178 9.57961345,12.9068174 C13.7562192,12.90237 16.9187977,9.71477 18.9955264,6.90995158 C19.1743758,6.66815165 19.1743758,6.3379173 18.9955264,6.09611737 C16.9186536,3.28603053 13.7570499,0.0989094737 9.57961345,0.0932647368 C9.57916871,0.0932643033 9.57872397,0.0932643033 9.57827924,0.0932647368 L9.57827924,0.0932647368 Z M9.57827924,1.46168579 L9.57961345,1.46168579 C12.8675916,1.46681737 15.5988249,3.96846211 17.5569468,6.50236737 C15.5985806,9.03230421 12.8660264,11.5341884 9.57961345,11.5383963 C6.29104758,11.5339489 3.55933568,9.03192789 1.60094367,6.49771474 C3.55930832,3.96777789 6.29185563,1.46589368 9.57827718,1.46168579 L9.57827924,1.46168579 Z" id="Shape"></path>
            <path d="M9.57894737,3.42075158 C7.88659608,3.42075158 6.5,4.80737263 6.5,6.49969895 C6.5,8.19205947 7.88659608,9.57864632 9.57894737,9.57864632 C11.2712987,9.57864632 12.6578947,8.19205947 12.6578947,6.49969895 C12.6578947,4.80737263 11.2712987,3.42075158 9.57894737,3.42075158 Z M9.57894737,4.78917263 C10.5317492,4.78917263 11.2894737,5.54690158 11.2894737,6.49969895 C11.2894737,7.45249632 10.5317492,8.21022526 9.57894737,8.21022526 C8.62614555,8.21022526 7.86842105,7.45249632 7.86842105,6.49969895 C7.86842105,5.54690158 8.62614555,4.78917263 9.57894737,4.78917263 Z" id="Shape"></path>
        </g>
    </g>
</g>
0
        </div>
            <div class="brick__image-inner "
                 id="js-image-3246"
                 v-lazy-bg="'https://madewithnetworkfra.fra1.digitaloceanspaces.com/spatie-space-production/28212/nuxt-speedkit.jpg'"
                style=" ">

            </div>



    </div>

    <div class="brick__caption">
                    <div class="brick__caption-upper">
            <a  href="https://madewithvuejs.com/nuxt-speedkit" class="brick__title">
                Nuxt Speedkit
            </a>
            <span class="brick__tagline">
            Performance Optimization Module
        </span>
        </div>
        <div class="brick__caption-lower">
            <div class="pull-left">

                                        <a href="/ssr">#SSR</a>
                                                                <a href="/utilities">#Utilities</a>
                                                        </div>
            <div class="pull-right">
        <div class="brick__views">
        <svg class="svg-inline u__va--middle" width="20px" height="13px" viewBox="0 0 20 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>icon-eye-dark</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
    <g id="Icons" transform="translate(-313.000000, -230.000000)" fill-rule="nonzero" fill="#435B71">
        <g id="icon-eye-dark" transform="translate(313.000000, 230.000000)">
            <path d="M9.57827924,0.0932647368 C5.40166592,0.0977121053 2.23909537,3.28527789 0.162366374,6.09013053 C-0.0164829778,6.33193046 -0.0164829778,6.66216481 0.162366374,6.90396474 C2.23923803,9.71401737 5.40085034,12.9011726 9.57827924,12.9068174 C9.57872397,12.9068178 9.57916871,12.9068178 9.57961345,12.9068174 C13.7562192,12.90237 16.9187977,9.71477 18.9955264,6.90995158 C19.1743758,6.66815165 19.1743758,6.3379173 18.9955264,6.09611737 C16.9186536,3.28603053 13.7570499,0.0989094737 9.57961345,0.0932647368 C9.57916871,0.0932643033 9.57872397,0.0932643033 9.57827924,0.0932647368 L9.57827924,0.0932647368 Z M9.57827924,1.46168579 L9.57961345,1.46168579 C12.8675916,1.46681737 15.5988249,3.96846211 17.5569468,6.50236737 C15.5985806,9.03230421 12.8660264,11.5341884 9.57961345,11.5383963 C6.29104758,11.5339489 3.55933568,9.03192789 1.60094367,6.49771474 C3.55930832,3.96777789 6.29185563,1.46589368 9.57827718,1.46168579 L9.57827924,1.46168579 Z" id="Shape"></path>
            <path d="M9.57894737,3.42075158 C7.88659608,3.42075158 6.5,4.80737263 6.5,6.49969895 C6.5,8.19205947 7.88659608,9.57864632 9.57894737,9.57864632 C11.2712987,9.57864632 12.6578947,8.19205947 12.6578947,6.49969895 C12.6578947,4.80737263 11.2712987,3.42075158 9.57894737,3.42075158 Z M9.57894737,4.78917263 C10.5317492,4.78917263 11.2894737,5.54690158 11.2894737,6.49969895 C11.2894737,7.45249632 10.5317492,8.21022526 9.57894737,8.21022526 C8.62614555,8.21022526 7.86842105,7.45249632 7.86842105,6.49969895 C7.86842105,5.54690158 8.62614555,4.78917263 9.57894737,4.78917263 Z" id="Shape"></path>
        </g>
    </g>
</g>
0
        </div>

3️⃣ Network latency

The network conditions of your users are halfway out of your control - but you can still account for them.

4️⃣ Code performance

How efficiently does your (Vue) code do what you want it to do?

5️⃣ Data requests / backend / APIs

If you need to load content from a CMS or a third party API to display in your app, your frontend performance is inevitably bound to the amount and performance of those API calls. Also, requests for analytics apps, ad networks and similar services can be costly.

6️⃣ Resource usage

How costly is your app in terms of memory and CPU usage? Depending on the device your users are on, this may impact performance a lot for them.

            <div class="brick__image-inner "
                 id="js-image-13"
                 v-lazy-bg="'https://madewithnetworkfra.fra1.digitaloceanspaces.com/spatie-space-production/28356/nuxt-3.jpg'"
                style="-webkit-background-size:contain;background-size:contain; background-position:center center;">

            </div>



    </div>

    <div class="brick__caption">
                    <div class="brick__caption-upper">
            <a  href="https://madewithvuejs.com/nuxt-js" class="brick__title">
                Nuxt.js
            </a>
            <span class="brick__tagline">
            Intuitive Vue Application Framework
        </span>
        </div>
        <div class="brick__caption-lower">
            <div class="pull-left">

                                        <a href="/frameworks">#Frameworks</a>
                                                                <a href="/ssr">#SSR</a>
                                                                <a href="/static-site-generator">#Static site generator</a>
                                                <span class="dots">...</span>
                                                        </div>
            <div class="pull-right">
        <div class="brick__views">
        <svg class="svg-inline u__va--middle" width="20px" height="13px" viewBox="0 0 20 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>icon-eye-dark</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
    <g id="Icons" transform="translate(-313.000000, -230.000000)" fill-rule="nonzero" fill="#435B71">
        <g id="icon-eye-dark" transform="translate(313.000000, 230.000000)">
            <path d="M9.57827924,0.0932647368 C5.40166592,0.0977121053 2.23909537,3.28527789 0.162366374,6.09013053 C-0.0164829778,6.33193046 -0.0164829778,6.66216481 0.162366374,6.90396474 C2.23923803,9.71401737 5.40085034,12.9011726 9.57827924,12.9068174 C9.57872397,12.9068178 9.57916871,12.9068178 9.57961345,12.9068174 C13.7562192,12.90237 16.9187977,9.71477 18.9955264,6.90995158 C19.1743758,6.66815165 19.1743758,6.3379173 18.9955264,6.09611737 C16.9186536,3.28603053 13.7570499,0.0989094737 9.57961345,0.0932647368 C9.57916871,0.0932643033 9.57872397,0.0932643033 9.57827924,0.0932647368 L9.57827924,0.0932647368 Z M9.57827924,1.46168579 L9.57961345,1.46168579 C12.8675916,1.46681737 15.5988249,3.96846211 17.5569468,6.50236737 C15.5985806,9.03230421 12.8660264,11.5341884 9.57961345,11.5383963 C6.29104758,11.5339489 3.55933568,9.03192789 1.60094367,6.49771474 C3.55930832,3.96777789 6.29185563,1.46589368 9.57827718,1.46168579 L9.57827924,1.46168579 Z" id="Shape"></path>
            <path d="M9.57894737,3.42075158 C7.88659608,3.42075158 6.5,4.80737263 6.5,6.49969895 C6.5,8.19205947 7.88659608,9.57864632 9.57894737,9.57864632 C11.2712987,9.57864632 12.6578947,8.19205947 12.6578947,6.49969895 C12.6578947,4.80737263 11.2712987,3.42075158 9.57894737,3.42075158 Z M9.57894737,4.78917263 C10.5317492,4.78917263 11.2894737,5.54690158 11.2894737,6.49969895 C11.2894737,7.45249632 10.5317492,8.21022526 9.57894737,8.21022526 C8.62614555,8.21022526 7.86842105,7.45249632 7.86842105,6.49969895 C7.86842105,5.54690158 8.62614555,4.78917263 9.57894737,4.78917263 Z" id="Shape"></path>
        </g>
    </g>
</g>
0
        </div>
                        <video
                    muted
                    autoplay
                    playsinline
                    loop
                    class="brick__video">
                <source src="https://madewithcode-dozlxc08.on-forge.com/storage/31702/vue-virtual-scroller-k.mp4" type="video/mp4">
            </video>


    </div>

    <div class="brick__caption">
                    <div class="brick__caption-upper">
            <a  href="https://madewithvuejs.com/vue-virtual-scroller" class="brick__title">
                Vue Virtual Scroller
            </a>
            <span class="brick__tagline">
            Blazing fast scrolling for any amount of data
        </span>
        </div>
        <div class="brick__caption-lower">
            <div class="pull-left">

                                        <a href="/utilities">#Utilities</a>
                                                                <a href="/loading">#Loading</a>
                                                        </div>
            <div class="pull-right">
        <div class="brick__views">
        <svg class="svg-inline u__va--middle" width="20px" height="13px" viewBox="0 0 20 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>icon-eye-dark</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
    <g id="Icons" transform="translate(-313.000000, -230.000000)" fill-rule="nonzero" fill="#435B71">
        <g id="icon-eye-dark" transform="translate(313.000000, 230.000000)">
            <path d="M9.57827924,0.0932647368 C5.40166592,0.0977121053 2.23909537,3.28527789 0.162366374,6.09013053 C-0.0164829778,6.33193046 -0.0164829778,6.66216481 0.162366374,6.90396474 C2.23923803,9.71401737 5.40085034,12.9011726 9.57827924,12.9068174 C9.57872397,12.9068178 9.57916871,12.9068178 9.57961345,12.9068174 C13.7562192,12.90237 16.9187977,9.71477 18.9955264,6.90995158 C19.1743758,6.66815165 19.1743758,6.3379173 18.9955264,6.09611737 C16.9186536,3.28603053 13.7570499,0.0989094737 9.57961345,0.0932647368 C9.57916871,0.0932643033 9.57872397,0.0932643033 9.57827924,0.0932647368 L9.57827924,0.0932647368 Z M9.57827924,1.46168579 L9.57961345,1.46168579 C12.8675916,1.46681737 15.5988249,3.96846211 17.5569468,6.50236737 C15.5985806,9.03230421 12.8660264,11.5341884 9.57961345,11.5383963 C6.29104758,11.5339489 3.55933568,9.03192789 1.60094367,6.49771474 C3.55930832,3.96777789 6.29185563,1.46589368 9.57827718,1.46168579 L9.57827924,1.46168579 Z" id="Shape"></path>
            <path d="M9.57894737,3.42075158 C7.88659608,3.42075158 6.5,4.80737263 6.5,6.49969895 C6.5,8.19205947 7.88659608,9.57864632 9.57894737,9.57864632 C11.2712987,9.57864632 12.6578947,8.19205947 12.6578947,6.49969895 C12.6578947,4.80737263 11.2712987,3.42075158 9.57894737,3.42075158 Z M9.57894737,4.78917263 C10.5317492,4.78917263 11.2894737,5.54690158 11.2894737,6.49969895 C11.2894737,7.45249632 10.5317492,8.21022526 9.57894737,8.21022526 C8.62614555,8.21022526 7.86842105,7.45249632 7.86842105,6.49969895 C7.86842105,5.54690158 8.62614555,4.78917263 9.57894737,4.78917263 Z" id="Shape"></path>
        </g>
    </g>
</g>
0
        </div>

7️⃣ Perceived performance

How snappy does the app feel to the user? Your user’s perception of how long it takes your app to load is actually even more important than the objective load time. It can be impacted by how loading resources is prioritized, handled and communicated.

                        <video
                    muted
                    autoplay
                    playsinline
                    loop
                    class="brick__video">
                <source src="https://madewithcode-dozlxc08.on-forge.com/storage/31280/vue-content-loader-k.mp4" type="video/mp4">
            </video>


    </div>

    <div class="brick__caption">
                    <div class="brick__caption-upper">
            <a  href="https://madewithvuejs.com/vue-content-loader" class="brick__title">
                Vue Content Loader
            </a>
            <span class="brick__tagline">
            SVG Loading Placeholder
        </span>
        </div>
        <div class="brick__caption-lower">
            <div class="pull-left">

                                        <a href="/ui-components">#UI Components</a>
                                                                <a href="/animation">#Animation</a>
                                                                <a href="/loading">#Loading</a>
                                                        </div>
            <div class="pull-right">
        <div class="brick__views">
        <svg class="svg-inline u__va--middle" width="20px" height="13px" viewBox="0 0 20 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>icon-eye-dark</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
    <g id="Icons" transform="translate(-313.000000, -230.000000)" fill-rule="nonzero" fill="#435B71">
        <g id="icon-eye-dark" transform="translate(313.000000, 230.000000)">
            <path d="M9.57827924,0.0932647368 C5.40166592,0.0977121053 2.23909537,3.28527789 0.162366374,6.09013053 C-0.0164829778,6.33193046 -0.0164829778,6.66216481 0.162366374,6.90396474 C2.23923803,9.71401737 5.40085034,12.9011726 9.57827924,12.9068174 C9.57872397,12.9068178 9.57916871,12.9068178 9.57961345,12.9068174 C13.7562192,12.90237 16.9187977,9.71477 18.9955264,6.90995158 C19.1743758,6.66815165 19.1743758,6.3379173 18.9955264,6.09611737 C16.9186536,3.28603053 13.7570499,0.0989094737 9.57961345,0.0932647368 C9.57916871,0.0932643033 9.57872397,0.0932643033 9.57827924,0.0932647368 L9.57827924,0.0932647368 Z M9.57827924,1.46168579 L9.57961345,1.46168579 C12.8675916,1.46681737 15.5988249,3.96846211 17.5569468,6.50236737 C15.5985806,9.03230421 12.8660264,11.5341884 9.57961345,11.5383963 C6.29104758,11.5339489 3.55933568,9.03192789 1.60094367,6.49771474 C3.55930832,3.96777789 6.29185563,1.46589368 9.57827718,1.46168579 L9.57827924,1.46168579 Z" id="Shape"></path>
            <path d="M9.57894737,3.42075158 C7.88659608,3.42075158 6.5,4.80737263 6.5,6.49969895 C6.5,8.19205947 7.88659608,9.57864632 9.57894737,9.57864632 C11.2712987,9.57864632 12.6578947,8.19205947 12.6578947,6.49969895 C12.6578947,4.80737263 11.2712987,3.42075158 9.57894737,3.42075158 Z M9.57894737,4.78917263 C10.5317492,4.78917263 11.2894737,5.54690158 11.2894737,6.49969895 C11.2894737,7.45249632 10.5317492,8.21022526 9.57894737,8.21022526 C8.62614555,8.21022526 7.86842105,7.45249632 7.86842105,6.49969895 C7.86842105,5.54690158 8.62614555,4.78917263 9.57894737,4.78917263 Z" id="Shape"></path>
        </g>
    </g>
</g>
0
        </div>
                        <video
                    muted
                    autoplay
                    playsinline
                    loop
                    class="brick__video">
                <source src="https://madewithcode-dozlxc08.on-forge.com/storage/31164/epic-spinners-k.mp4" type="video/mp4">
            </video>


    </div>

    <div class="brick__caption">
                    <div class="brick__caption-upper">
            <a  href="https://madewithvuejs.com/epic-spinners" class="brick__title">
                Epic spinners
            </a>
            <span class="brick__tagline">
            Loading Spinner Collection
        </span>
        </div>
        <div class="brick__caption-lower">
            <div class="pull-left">

                                        <a href="/ui-components">#UI Components</a>
                                                                <a href="/animation">#Animation</a>
                                                                <a href="/loading">#Loading</a>
                                                        </div>
            <div class="pull-right">
        <div class="brick__views">
        <svg class="svg-inline u__va--middle" width="20px" height="13px" viewBox="0 0 20 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>icon-eye-dark</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
    <g id="Icons" transform="translate(-313.000000, -230.000000)" fill-rule="nonzero" fill="#435B71">
        <g id="icon-eye-dark" transform="translate(313.000000, 230.000000)">
            <path d="M9.57827924,0.0932647368 C5.40166592,0.0977121053 2.23909537,3.28527789 0.162366374,6.09013053 C-0.0164829778,6.33193046 -0.0164829778,6.66216481 0.162366374,6.90396474 C2.23923803,9.71401737 5.40085034,12.9011726 9.57827924,12.9068174 C9.57872397,12.9068178 9.57916871,12.9068178 9.57961345,12.9068174 C13.7562192,12.90237 16.9187977,9.71477 18.9955264,6.90995158 C19.1743758,6.66815165 19.1743758,6.3379173 18.9955264,6.09611737 C16.9186536,3.28603053 13.7570499,0.0989094737 9.57961345,0.0932647368 C9.57916871,0.0932643033 9.57872397,0.0932643033 9.57827924,0.0932647368 L9.57827924,0.0932647368 Z M9.57827924,1.46168579 L9.57961345,1.46168579 C12.8675916,1.46681737 15.5988249,3.96846211 17.5569468,6.50236737 C15.5985806,9.03230421 12.8660264,11.5341884 9.57961345,11.5383963 C6.29104758,11.5339489 3.55933568,9.03192789 1.60094367,6.49771474 C3.55930832,3.96777789 6.29185563,1.46589368 9.57827718,1.46168579 L9.57827924,1.46168579 Z" id="Shape"></path>
            <path d="M9.57894737,3.42075158 C7.88659608,3.42075158 6.5,4.80737263 6.5,6.49969895 C6.5,8.19205947 7.88659608,9.57864632 9.57894737,9.57864632 C11.2712987,9.57864632 12.6578947,8.19205947 12.6578947,6.49969895 C12.6578947,4.80737263 11.2712987,3.42075158 9.57894737,3.42075158 Z M9.57894737,4.78917263 C10.5317492,4.78917263 11.2894737,5.54690158 11.2894737,6.49969895 C11.2894737,7.45249632 10.5317492,8.21022526 9.57894737,8.21022526 C8.62614555,8.21022526 7.86842105,7.45249632 7.86842105,6.49969895 C7.86842105,5.54690158 8.62614555,4.78917263 9.57894737,4.78917263 Z" id="Shape"></path>
        </g>
    </g>
</g>
0
        </div>

Tools to measure and optimize performance

Performance profiling

To check your app’s current performance (and get some insights about possible improvements) you can use the following tools:

                        <video
                    muted
                    autoplay
                    playsinline
                    loop
                    class="brick__video">
                <source src="https://madewithnetworkfra.fra1.digitaloceanspaces.com/spatie-space-production/2405/vue-devtools-v3.mp4" type="video/mp4">
            </video>


    </div>

    <div class="brick__caption">
                    <div class="brick__caption-upper">
            <a  href="https://madewithvuejs.com/vue-devtools-v3" class="brick__title">
                Vue Devtools
            </a>
            <span class="brick__tagline">
            Debugging Tools for Vue Apps
        </span>
        </div>
        <div class="brick__caption-lower">
            <div class="pull-left">

                                        <a href="/dev-tools">#Dev Tools</a>
                                                                <a href="/testing">#Testing</a>
                                                                <a href="/browser-extension">#Browser extension</a>
                                                <span class="dots">...</span>
                                                        </div>
            <div class="pull-right">
        <div class="brick__views">
        <svg class="svg-inline u__va--middle" width="20px" height="13px" viewBox="0 0 20 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>icon-eye-dark</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
    <g id="Icons" transform="translate(-313.000000, -230.000000)" fill-rule="nonzero" fill="#435B71">
        <g id="icon-eye-dark" transform="translate(313.000000, 230.000000)">
            <path d="M9.57827924,0.0932647368 C5.40166592,0.0977121053 2.23909537,3.28527789 0.162366374,6.09013053 C-0.0164829778,6.33193046 -0.0164829778,6.66216481 0.162366374,6.90396474 C2.23923803,9.71401737 5.40085034,12.9011726 9.57827924,12.9068174 C9.57872397,12.9068178 9.57916871,12.9068178 9.57961345,12.9068174 C13.7562192,12.90237 16.9187977,9.71477 18.9955264,6.90995158 C19.1743758,6.66815165 19.1743758,6.3379173 18.9955264,6.09611737 C16.9186536,3.28603053 13.7570499,0.0989094737 9.57961345,0.0932647368 C9.57916871,0.0932643033 9.57872397,0.0932643033 9.57827924,0.0932647368 L9.57827924,0.0932647368 Z M9.57827924,1.46168579 L9.57961345,1.46168579 C12.8675916,1.46681737 15.5988249,3.96846211 17.5569468,6.50236737 C15.5985806,9.03230421 12.8660264,11.5341884 9.57961345,11.5383963 C6.29104758,11.5339489 3.55933568,9.03192789 1.60094367,6.49771474 C3.55930832,3.96777789 6.29185563,1.46589368 9.57827718,1.46168579 L9.57827924,1.46168579 Z" id="Shape"></path>
            <path d="M9.57894737,3.42075158 C7.88659608,3.42075158 6.5,4.80737263 6.5,6.49969895 C6.5,8.19205947 7.88659608,9.57864632 9.57894737,9.57864632 C11.2712987,9.57864632 12.6578947,8.19205947 12.6578947,6.49969895 C12.6578947,4.80737263 11.2712987,3.42075158 9.57894737,3.42075158 Z M9.57894737,4.78917263 C10.5317492,4.78917263 11.2894737,5.54690158 11.2894737,6.49969895 C11.2894737,7.45249632 10.5317492,8.21022526 9.57894737,8.21022526 C8.62614555,8.21022526 7.86842105,7.45249632 7.86842105,6.49969895 C7.86842105,5.54690158 8.62614555,4.78917263 9.57894737,4.78917263 Z" id="Shape"></path>
        </g>
    </g>
</g>
0
        </div>

For more insights on bundle size:

Performance budgeting

If you want to make performance a priority, it makes sense to set yourself budgets for the most important metrics like pagespeed and bundle size, and measure your app against them regularly.

Bundle size budgeting

To keep your bundle size in check, you can use CLI tools like siddharthkp/bundlesize to set up automated checks for your build pipeline. Tools like Packtracker (#madewithvuejs!) offer an additional dashboard visualizing bundle size over time.

                        <video
                    muted
                    autoplay
                    playsinline
                    loop
                    class="brick__video">
                <source src="https://madewithnetworkfra.fra1.digitaloceanspaces.com/spatie-space-production/3258/packtracker-k.mp4" type="video/mp4">
            </video>


    </div>

    <div class="brick__caption">
                    <div class="brick__caption-upper">
            <a  href="https://madewithvuejs.com/packtracker" class="brick__title">
                Packtracker
            </a>
            <span class="brick__tagline">
            Webpack Bundle Monitoring
        </span>
        </div>
        <div class="brick__caption-lower">
            <div class="pull-left">

                                        <a href="/webapps">#Webapps</a>
                                                                <a href="/dev-tools">#Dev Tools</a>
                                                                <a href="/analytics">#Analytics</a>
                                                        </div>
            <div class="pull-right">
        <div class="brick__views">
        <svg class="svg-inline u__va--middle" width="20px" height="13px" viewBox="0 0 20 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>icon-eye-dark</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
    <g id="Icons" transform="translate(-313.000000, -230.000000)" fill-rule="nonzero" fill="#435B71">
        <g id="icon-eye-dark" transform="translate(313.000000, 230.000000)">
            <path d="M9.57827924,0.0932647368 C5.40166592,0.0977121053 2.23909537,3.28527789 0.162366374,6.09013053 C-0.0164829778,6.33193046 -0.0164829778,6.66216481 0.162366374,6.90396474 C2.23923803,9.71401737 5.40085034,12.9011726 9.57827924,12.9068174 C9.57872397,12.9068178 9.57916871,12.9068178 9.57961345,12.9068174 C13.7562192,12.90237 16.9187977,9.71477 18.9955264,6.90995158 C19.1743758,6.66815165 19.1743758,6.3379173 18.9955264,6.09611737 C16.9186536,3.28603053 13.7570499,0.0989094737 9.57961345,0.0932647368 C9.57916871,0.0932643033 9.57872397,0.0932643033 9.57827924,0.0932647368 L9.57827924,0.0932647368 Z M9.57827924,1.46168579 L9.57961345,1.46168579 C12.8675916,1.46681737 15.5988249,3.96846211 17.5569468,6.50236737 C15.5985806,9.03230421 12.8660264,11.5341884 9.57961345,11.5383963 C6.29104758,11.5339489 3.55933568,9.03192789 1.60094367,6.49771474 C3.55930832,3.96777789 6.29185563,1.46589368 9.57827718,1.46168579 L9.57827924,1.46168579 Z" id="Shape"></path>
            <path d="M9.57894737,3.42075158 C7.88659608,3.42075158 6.5,4.80737263 6.5,6.49969895 C6.5,8.19205947 7.88659608,9.57864632 9.57894737,9.57864632 C11.2712987,9.57864632 12.6578947,8.19205947 12.6578947,6.49969895 C12.6578947,4.80737263 11.2712987,3.42075158 9.57894737,3.42075158 Z M9.57894737,4.78917263 C10.5317492,4.78917263 11.2894737,5.54690158 11.2894737,6.49969895 C11.2894737,7.45249632 10.5317492,8.21022526 9.57894737,8.21022526 C8.62614555,8.21022526 7.86842105,7.45249632 7.86842105,6.49969895 C7.86842105,5.54690158 8.62614555,4.78917263 9.57894737,4.78917263 Z" id="Shape"></path>
        </g>
    </g>
</g>
0
        </div>

Lighthouse audits & performance budgets

The Lighthouse CI CLI tool and GitHub action lets you analyze your app continuously in your workflows and set up performance budgets for the metrics you care about.

(Fullstack) Performance monitoring

If you want to take it a step further, performance monitoring helps you keep track of your app’s performance over time and notifies you about issues as soon as they crop up.

Monitoring services provide insights about where and how to solve issues. They track your app as a whole, and help you identify the source of issues – is your frontend, backend, or API xyz at fault? Sentry is our weapon of choice. We use their Vue and Laravel SDKs, but they support almost every other stack as well!

            <div class="brick__image-inner "
                 id="js-image-50"
                 v-lazy-bg="'https://madewithnetworkfra.fra1.digitaloceanspaces.com/spatie-space-production/29802/sentry-vue-7.jpg'"
                style=" ">

            </div>



    </div>

    <div class="brick__caption">
                    <div class="brick__caption-upper">
            <a  href="https://madewithvuejs.com/sentry-for-vue" class="brick__title">
                Sentry for Vue
            </a>
            <span class="brick__tagline">
            Vue Error &amp; Performance Monitoring
        </span>
        </div>
        <div class="brick__caption-lower">
            <div class="pull-left">

                                        <a href="/dev-tools">#Dev Tools</a>
                                                                <a href="/integration">#Integration</a>
                                                                <a href="/testing">#Testing</a>
                                                <span class="dots">...</span>
                                                        </div>
            <div class="pull-right">
        <div class="brick__views">
        <svg class="svg-inline u__va--middle" width="20px" height="13px" viewBox="0 0 20 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>icon-eye-dark</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
    <g id="Icons" transform="translate(-313.000000, -230.000000)" fill-rule="nonzero" fill="#435B71">
        <g id="icon-eye-dark" transform="translate(313.000000, 230.000000)">
            <path d="M9.57827924,0.0932647368 C5.40166592,0.0977121053 2.23909537,3.28527789 0.162366374,6.09013053 C-0.0164829778,6.33193046 -0.0164829778,6.66216481 0.162366374,6.90396474 C2.23923803,9.71401737 5.40085034,12.9011726 9.57827924,12.9068174 C9.57872397,12.9068178 9.57916871,12.9068178 9.57961345,12.9068174 C13.7562192,12.90237 16.9187977,9.71477 18.9955264,6.90995158 C19.1743758,6.66815165 19.1743758,6.3379173 18.9955264,6.09611737 C16.9186536,3.28603053 13.7570499,0.0989094737 9.57961345,0.0932647368 C9.57916871,0.0932643033 9.57872397,0.0932643033 9.57827924,0.0932647368 L9.57827924,0.0932647368 Z M9.57827924,1.46168579 L9.57961345,1.46168579 C12.8675916,1.46681737 15.5988249,3.96846211 17.5569468,6.50236737 C15.5985806,9.03230421 12.8660264,11.5341884 9.57961345,11.5383963 C6.29104758,11.5339489 3.55933568,9.03192789 1.60094367,6.49771474 C3.55930832,3.96777789 6.29185563,1.46589368 9.57827718,1.46168579 L9.57827924,1.46168579 Z" id="Shape"></path>
            <path d="M9.57894737,3.42075158 C7.88659608,3.42075158 6.5,4.80737263 6.5,6.49969895 C6.5,8.19205947 7.88659608,9.57864632 9.57894737,9.57864632 C11.2712987,9.57864632 12.6578947,8.19205947 12.6578947,6.49969895 C12.6578947,4.80737263 11.2712987,3.42075158 9.57894737,3.42075158 Z M9.57894737,4.78917263 C10.5317492,4.78917263 11.2894737,5.54690158 11.2894737,6.49969895 C11.2894737,7.45249632 10.5317492,8.21022526 9.57894737,8.21022526 C8.62614555,8.21022526 7.86842105,7.45249632 7.86842105,6.49969895 C7.86842105,5.54690158 8.62614555,4.78917263 9.57894737,4.78917263 Z" id="Shape"></path>
        </g>
    </g>
</g>
0
        </div>

To get started, we can recommend the free 🎓️ Vueschool course on how to implement application monitoring with Sentry, including a chapter about performance monitoring.

Find more infos about advanced Vue.js performance monitoring in our follow-up article!

More resources about (Vue) performance

Armin Ulrich
Written by
Armin Ulrich
Posted on
June 15, 2022
Facebook this Tweet This
Similar Articles
Best Vue Form Libraries of 2024
March 26, 2024  •  in #UI Components
Forms are an important ingredient for almost every web app. But they can get complex pretty fast, and you’ll have to work through the repetitive processes of handling, storing and validating your data for every custom form. At some point, you'll be...
Set up easy Vue Pagination for Laravel Projects [via Cloudways]
August 24, 2018  •  in #Tutorial, #Bookmarks
In his tutorial for Cloudways, Pardeep Kumar shows you how to easily create a pagination with Vue and Laravel!
COMMUNITY SPONSORS