← Back to Web application starters · project planning pack

Skill · Version 1.0.0

Fast Vue interfaces and consistent rendering

Improves slow Vue views by reducing unnecessary work and keeping server-generated pages consistent with the browser’s first render.

vue-performance-ssr

Agents using this skill

See how it helps

When is it useful?

Use when large tables, heavy dialogs or server-rendered pages feel sluggish or change unexpectedly during startup.

What does it help you do?

  • Reduces unnecessary component updates and uses suitable handling for large read-only datasets.
  • Loads heavy secondary features when needed and keeps large lists manageable.
  • Finds mismatched initial content and request-shared state that can affect server-rendered pages.

Try asking this

Improve a Vue product table with thousands of records and a rich editor dialog. Measure updates, defer the editor load and check the initial server-rendered page. Compare table interaction before and after optimization and inspect the initial bundle. The editor loads when needed, while server and client render the same initial content.

Detailed documentation

Read the skill’s original instructions, examples and checks in their authored language.

Details…Show less

Vue Performance & SSR

Keep Vue interfaces fast and predictable by reducing reactivity overhead, stabilizing component props, splitting heavy bundles, and keeping server and client rendering consistent.

1. Reactivity and rendering efficiency

  • Keep props stable: avoid passing newly constructed object/array literals or inline values that change every parent render when child updates are expensive. For active-item lists (such as <ListItem :is-active="item.id === activeId">), pass the boolean :is-active prop rather than :active-id="activeId", so changing activeId re-renders only the 2 affected items instead of all $N$ items.
  • Avoid deep reactivity on large or opaque data: use shallowRef() for large arrays of read-only records (hundreds or thousands of rows), geojson/chart datasets, or third-party SDK instances (markRaw() for non-reactive objects stored inside reactive state).
  • v-memo and v-once: in hot list templates where item rendering is expensive, use v-memo="[item.id, item.updatedAt, item.id === selectedId]" to skip virtual DOM diffing when dependencies have not changed. Use v-once only for strictly static subtrees.
  • Large lists: never combine v-if and v-for on the same element — pre-filter the list in a computed property. For lists over ~500 visible DOM rows, prefer pagination, windowed rendering or virtual scrolling (vue-virtual-scroller / @tanstack/vue-virtual).

2. Bundle size and async components

  • Lazy-load heavy, below-the-fold or modal-only components (rich text editors, chart libraries, PDF viewers, code editors) with defineAsyncComponent(() => import('./HeavyEditor.vue')) and provide a lightweight loading/error fallback.
  • Import specific utilities and icons rather than pulling entire barrel bundles into initial routes when tree-shaking is not guaranteed.

3. SSR and hydration mismatch prevention

  • When the app uses SSR (Nuxt, Inertia SSR, or custom createSSRApp), the initial client render must produce identical DOM structure and text to the server HTML:
    • Never read window, document, localStorage, navigator or viewport dimensions inside <script setup> top-level execution or computed properties used during initial render — defer browser-only reads to onMounted().
    • Avoid non-deterministic values during render (Math.random(), new Date() formatted with client-local timezone, auto-incrementing counters); use useId() (Vue 3.5+) for SSR-safe accessible element IDs.
    • Keep valid HTML nesting (<div> inside <p>, <tbody> omitted in <table>, or interactive <button> nested inside <button>/<a> causes the browser HTML parser to repair the DOM and breaks hydration).
  • Avoid cross-request state pollution: in SSR, module-level ref() or reactive() singletons are shared across all incoming server requests and leak user data! Create app, router and Pinia instances per request, and keep state inside components, Pinia stores or request-scoped provide/inject.

Before you finish

  • Large read-only datasets or third-party instances use shallowRef / markRaw where appropriate.
  • List items receive stable :keys and primitive/stable props that avoid re-rendering the entire list on selection change.
  • Heavy modal or secondary libraries are lazy-loaded with defineAsyncComponent or route-level code splitting.
  • In SSR apps, no browser globals, random IDs or module-level mutable singletons run during server render.

Included files 2

  • LICENSE.txt
  • SKILL.md