Template Refs & defineExpose()

πŸ’š Vue.js 3 🟒 Chapter 21 of 30 πŸ“‚ Phase 06: Lifecycle & Advanced Composition API πŸ“… 2026 Edition
πŸ“Œ Covered in this chapter: Template Refs with ref() Β· DOM Element Access Β· Component Instance Refs Β· defineExpose() Public API Β· Function Refs

Welcome to Chapter 21: Template Refs & defineExpose() in our Vue.js 3 Masterclass Roadmap! Vue.js is a progressive, approachable, and performant framework for building modern single-page applications (SPAs) and interactive web user interfaces.

1Core Architectural Concepts of Template Refs & defineExpose()

In Vue.js 3, understanding Template Refs & defineExpose() is vital for building reactive, declarative, and scalable front-end components. Vue combines an intuitive template-based syntax with an optimized Virtual DOM engine and proxy-based reactivity system.

Key Architectural Takeaway: Vue 3 uses ES6 Proxies to track reactive state dependencies automatically. When state changes, Vue computes minimal virtual DOM diffs and batches real DOM updates efficiently.
  • Declarative Rendering: Vue extends standard HTML with directive attributes to declaratively bind reactive JavaScript state to the DOM.
  • Component Driven: Applications are composed of self-contained Single File Components (.vue SFCs) encapsulating template, logic, and scoped styles.
  • Reactivity Engine: Primitive reactive references (ref()) and reactive proxy objects (reactive()) allow automatic view recalculations without manual DOM manipulation.
2Annotated Code Walkthrough & Implementation

Let us examine a complete single-file component implementation demonstrating Template Refs & defineExpose() using Vue 3 Composition API and <script setup> syntax:

Vue 3 Single File Component (.vue) β–Ά Run in Web Playground
<script setup>
import { ref, computed } from 'vue';

// Define reactive state for Template Refs & defineExpose()
const title = ref('Template Refs & defineExpose() Demonstration');
const count = ref(0);
const items = ref(['Vue 3 Composition API', 'Vite Bundler', 'Pinia State']);

const doubleCount = computed(() => count.value * 2);

function increment() {
  count.value++;
}
</script>

<template>
  <div class="vue-card">
    <h2>{{ title }}</h2>
    <p>Current Counter: <strong>{{ count }}</strong> (Double: {{ doubleCount }})</p>
    <button @click="increment" class="btn">βž• Increment Counter</button>

    <ul style="margin-top: 12px;">
      <li v-for="(item, idx) in items" :key="idx">{{ item }}</li>
    </ul>
  </div>
</template>

<style scoped>
.vue-card {
  background: var(--bg2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 16px;
  color: var(--text);
}
.btn {
  background: #42b883;
  color: #ffffff;
  border: none;
  padding: 8px 16px;
  border-radius: 6px;
  font-weight: 600;
  cursor: pointer;
}
</style>

In the Composition API snippet above, notice how ref() tracks reactive primitive state while template event listeners (@click) automatically trigger interface re-renders.

3Technical Feature Matrix & Specification Table

Review the comparative specification table below to understand how Vue 3 features operate across development scenarios:

Vue ConceptReactivity MechanismCompilation BehaviorBest Use Case
ref()Proxy wrapper (.value)Unwrapped automatically in templatesPrimitives (strings, numbers, booleans)
reactive()Deep ES6 Proxy objectDirect property accessComplex nested state objects / collections
computed()Cached getter proxyRecalculates only when dependencies changeDerived data & filtered arrays
watch()Explicit source listenerRuns side effects on state mutationAsync API calls, local storage syncing
4Production Architecture & Enterprise Patterns

When engineering scalable frontend applications with Vue 3, adhere to production architecture best practices:

Vue 3 β€” Production Composable Pattern β–Ά Run in Web Playground
// composables/useFetchData.js
import { ref } from 'vue';

export function useFetchData(url) {
  const data = ref(null);
  const error = ref(null);
  const loading = ref(true);

  async function fetchData() {
    loading.value = true;
    try {
      const res = await fetch(url);
      data.value = await res.json();
    } catch (err) {
      error.value = err.message;
    } finally {
      loading.value = false;
    }
  }

  fetchData();

  return { data, error, loading, refetch: fetchData };
}
5Common Developer Pitfalls & Solutions

Avoid these common beginner and intermediate Vue developer mistakes:

  • Pitfall 1: Forgetting .value in Script. Accessing a ref() inside <script> without .value returns the Ref object rather than its inner primitive. Solution: Always access myRef.value inside JavaScript code.
  • Pitfall 2: Destructuring reactive() Objects. Destructuring properties directly from a reactive() object breaks reactivity tracking. Solution: Use toRefs(myReactiveObj) before destructuring.
  • Pitfall 3: Mutating Props Directly. Mutating a prop in a child component violates one-way data flow. Solution: Emit a custom event (emit('update:propName')) or use defineModel().
6Frequently Asked Questions (FAQ)

Q1 Why choose Vue 3 over React or Angular?

Vue 3 strikes an optimal balance: it offers an approachable learning curve with optional template syntax, progressive adoption, official state management (Pinia) and routing (Vue Router), and top-tier Composition API performance.

Q2 What is the difference between ref() and reactive()?

ref() holds single primitive values (or objects) accessed via .value, while reactive() creates deep proxy objects for structured state objects.

Q3 What is <script setup> in Vue 3?

<script setup> is a compile-time syntactic sugar macro that drastically reduces boilerplate code when writing Composition API single-file components.

Q4 Is Vue 2 still supported?

Vue 2 reached official End-of-Life (EOL) in December 2023. All modern production projects should be built on Vue 3.

Q5 Where can I test Vue code snippets?

Click the β–Ά Run in Web Playground button on any code block in this tutorial to open our interactive HTML/CSS/JS online editor!

OC
Written by Our Compiler Technical Editorial Team
Reviewed for accuracy & tested on Vue 3.4+ (Vite 5) Β· Last updated August 2026