Related reads: Emmet Tips & Tricks for Beginners, Exploring New CSS Features in 2025, Vue.js: 10 Tips & Tricks, and Understanding JavaScript Scope: A Beginner’s Guide.
What we’ll cover (quick overview)
- What Vue is and when to use it (Vue Docs)
- Two fast start paths: CDN and Vite (Vite Docs)
- Reactivity + Composition API mental model (Vue Docs)
- Components, props & emits
- Routing with Vue Router (Vue Router Docs)
- Global state with Pinia (Pinia Docs)
- Fetching data (loading/error patterns) — pair with JavaScript Promise: What It Is and How to Use It
- Forms &
v-model - Devtools, build & deploy (including WordPress) — see Building a Block Theme in 2025
- Common pitfalls & next steps
What is Vue—and when should you use it?
Vue is a progressive framework for building interactive UIs. It’s small, fast, and scales from a single widget to full apps. Reach for Vue when you need rich interactivity (filters, tabs, forms, dashboards) without heavy ceremony. (Overview: Vue Docs)
Two quick ways to start (CDN vs. Vite)
Option A — One-file demo via CDN (fastest)
Reference: Vue Docs
<div id="app">
<h2>{{ title }}</h2>
<button @click="count++">Clicked {{ count }} times</button>
</div>
<script type="module">
import { createApp, ref } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
createApp({
setup() {
const title = ref('Hello Vue 👋')
const count = ref(0)
return { title, count }
}
}).mount('#app')
</script>
Option B — Vite + Single File Components (production-ready)
Starter: Vite Docs
# Node 18+ recommended
npm create vite@latest my-vue-app -- --template vue
cd my-vue-app
npm install
npm run dev
This gives you Single File Components (.vue), hot reload, TypeScript support, and a simple build step.
Core mental model: reactivity + the Composition API
Concepts: Composition API FAQ
<script setup>
import { ref, computed, watch } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
watch(count, n => console.log('count changed to', n))
</script>
<template>
<button @click="count++">
{{ count }} (x2 = {{ doubled }})
</button>
</template>
Tips: Use ref for primitives, reactive() for objects, prefer computed over watch for derivations.
Components, props & emits (parent/child flow)
Guide: Props & one-way data flow
Child component
<!-- src/components/TodoItem.vue -->
<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({ item: Object })
const emit = defineEmits(['toggle'])
</script>
<template>
<label>
<input type="checkbox" :checked="item.done" @change="emit('toggle', item.id)" />
<span :style="{ textDecoration: item.done ? 'line-through' : 'none' }">{{ item.title }}</span>
</label>
</template>
Parent component
<script setup>
import { reactive } from 'vue'
import TodoItem from './TodoItem.vue'
const state = reactive({
todos: [
{ id: 1, title: 'Learn Vue', done: false },
{ id: 2, title: 'Ship feature', done: true }
]
})
function toggle(id){
const t = state.todos.find(t => t.id === id)
t.done = !t.done
}
</script>
<template>
<TodoItem v-for="t in state.todos" :key="t.id" :item="t" @toggle="toggle" />
</template>
Routing with Vue Router
Docs: Vue Router
npm i vue-router
// src/router.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from './pages/Home.vue'
import About from './pages/About.vue'
export const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
})
// src/main.js
import { createApp } from 'vue'
import { router } from './router'
import App from './App.vue'
createApp(App).use(router).mount('#app')
Global state with Pinia (lightweight & type-friendly)
Docs: Pinia
npm i pinia
// src/stores/useTodos.js
import { defineStore } from 'pinia'
export const useTodos = defineStore('todos', {
state: () => ({ items: [] }),
actions: {
add(title){ this.items.push({ id: Date.now(), title, done: false }) }
},
getters: {
remaining: (s) => s.items.filter(i => !i.done).length
}
})
Fetching data (loading/error patterns)
Brush up on async flows with JavaScript Promise: What It Is and How to Use It—it pairs nicely with the pattern below.
<script setup>
import { ref, onMounted } from 'vue'
const users = ref([])
const loading = ref(false)
const error = ref(null)
onMounted(async () => {
loading.value = true
try {
const res = await fetch('https://jsonplaceholder.typicode.com/users')
if (!res.ok) throw new Error('Network error')
users.value = await res.json()
} catch (e) {
error.value = e.message
} finally {
loading.value = false
}
})
</script>
<template>
<p v-if="loading">Loading…</p>
<p v-else-if="error">Error: {{ error }}</p>
<ul v-else>
<li v-for="u in users" :key="u.id">{{ u.name }}</li>
</ul>
</template>
Forms & v-model
Guide: Forms
<script setup>
import { ref } from 'vue'
const form = ref({ name: '', newsletter: false })
</script>
<template>
<input v-model="form.name" placeholder="Your name" />
<label><input type="checkbox" v-model="form.newsletter" /> Subscribe</label>
<pre>{{ form }}</pre>
</template>
Devtools, build & deploy (including WordPress)
- Devtools: Install the Vue Devtools browser extension to inspect component state.
- Build:
npm run build(Vite build) - WordPress: Great for mounting Vue widgets in themes/plugins. For a WordPress-first path, see Building a Block Theme in 2025.
Enqueue your compiled script and mount point
// functions.php (theme) or main plugin file
add_action('wp_enqueue_scripts', function () {
wp_enqueue_script(
'my-vue-app',
get_stylesheet_directory_uri() . '/assets/my-vue-app/dist/assets/index.js',
array(), null, true
);
});
<div id="my-vue-app"></div>
Common pitfalls (and how to avoid them)
- Missing
:keyinv-for(use stable IDs). List rendering: Vue Docs - Overusing
watch—prefercomputedfor derivations. Computed - Mutating props directly—emit events or use a store. Props are read-only
- Performance—lazy-load routes & split chunks. Lazy loading
Next steps
- Build a tiny widget first, then layer in Router and Pinia.
- Revisit fundamentals with Understanding JavaScript Scope and modern styling in Exploring New CSS Features in 2025.
- Speed up authoring with Emmet Tips & Tricks for Beginners. When you’re ready for more Vue patterns, read Vue.js: 10 Tips & Tricks.
Bonus: quick starter scaffold
npm create vite@latest vue-starter -- --template vue
cd vue-starter && npm i && npm run dev
src/App.vue
<script setup>
import { ref } from 'vue'
const name = ref('Riad')
</script>
<template>
<main>
<h1>Vue Starter</h1>
<input v-model="name" />
<p>Hello, {{ name }}</p>
</main>
</template>
<style scoped>
main { max-width: 720px; margin: 3rem auto; font: 16px/1.6 system-ui, sans-serif; }
input { padding: .5rem .75rem; }
</style>
src/main.js
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
Leave a Reply