Chapter 109: Vue Examples
Vue Examples — 7 real-world, copy-paste-ready components that use almost everything we’ve covered so far (props, emits, slots, v-model, v-for, v-if/v-show, computed, watch, lifecycle, expose, KeepAlive, Transition, Teleport, Suspense…)
Each example is small enough to understand quickly, but realistic enough to use in actual projects.
Ready? Let’s go — one beautiful component at a time.
1. Reusable Modal with v-model + expose + slots + Transition + Teleport
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
<!-- Modal.vue --> <template> <Teleport to="body"> <Transition name="modal"> <div v-if="modelValue" class="modal-overlay" @click.self="close"> <div class="modal-content" @click.stop> <!-- Named slots --> <div class="modal-header"> <slot name="header"> <h2>{{ title || 'Confirmation' }}</h2> </slot> <button class="close-btn" @click="close">×</button> </div> <div class="modal-body"> <slot /> </div> <div class="modal-footer"> <slot name="footer"> <button @click="close">Cancel</button> <button class="confirm" @click="confirm">Confirm</button> </slot> </div> </div> </div> </Transition> </Teleport> </template> <script setup lang="ts"> import { ref, watch } from 'vue' const props = defineProps<{ modelValue: boolean title?: string }>() const emit = defineEmits<{ (e: 'update:modelValue', value: boolean): void (e: 'confirm'): void }>() const isVisible = ref(props.modelValue) watch(() => props.modelValue, val => { isVisible.value = val }) function close() { emit('update:modelValue', false) } function confirm() { emit('confirm') close() } // Expose public API defineExpose({ open: () => emit('update:modelValue', true), close, confirm }) </script> <style scoped> .modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.6); display: flex; align-items: center; justify-content: center; z-index: 1000; } .modal-content { background: white; border-radius: 12px; width: 90%; max-width: 500px; max-height: 90vh; overflow-y: auto; box-shadow: 0 20px 50px rgba(0,0,0,0.3); } .modal-header { padding: 1.5rem; border-bottom: 1px solid #e5e7eb; display: flex; justify-content: space-between; align-items: center; } .close-btn { font-size: 1.8rem; background: none; border: none; cursor: pointer; } .modal-body { padding: 1.5rem; } .modal-footer { padding: 1rem 1.5rem; border-top: 1px solid #e5e7eb; text-align: right; } .confirm { background: #3b82f6; color: white; padding: 0.6rem 1.2rem; border: none; border-radius: 6px; cursor: pointer; margin-left: 0.8rem; } .modal-enter-active, .modal-leave-active { transition: all 0.3s ease; } .modal-enter-from, .modal-leave-to { opacity: 0; transform: scale(0.95); } </style> |
Parent usage
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<Modal v-model="showModal" title="Delete Project?" @confirm="deleteProject"> <p>Are you sure? This cannot be undone.</p> </Modal> <button @click="showModal = true">Delete Project</button> <script setup> import { ref } from 'vue' import Modal from '@/components/Modal.vue' const showModal = ref(false) function deleteProject() { console.log('Project deleted!') showModal.value = false } </script> |
2. Todo Item with TransitionGroup + Transition + emits + v-model + slots
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<!-- TodoItem.vue --> <template> <div class="todo-item"> <input type="checkbox" :checked="done" @change="emit('toggle', id)" /> <!-- Scoped slot for text – parent can customize rendering --> <slot name="text" :text="text" :done="done"> <span :class="{ done }">{{ text }}</span> </slot> <button class="delete" @click="emit('delete', id)">×</button> </div> </template> <script setup lang="ts"> defineProps<{ id: number text: string done: boolean }>() defineEmits<{ (e: 'toggle', id: number): void (e: 'delete', id: number): void }>() </script> |
Parent – Todo List with animation
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<template> <TransitionGroup name="list" tag="ul"> <TodoItem v-for="todo in todos" :key="todo.id" :id="todo.id" :text="todo.text" :done="todo.done" @toggle="toggleTodo" @delete="deleteTodo" > <!-- Custom text rendering using scoped slot --> <template #text="{ text, done }"> <span :class="{ done }">{{ text }}</span> <small v-if="done">(completed)</small> </template> </TodoItem> </TransitionGroup> </template> |
3. Quick Reference – Which example shows what?
| Component / Pattern | Key Vue features shown |
|---|---|
| Modal | v-model, defineEmits, defineExpose, slots, Transition, Teleport |
| TodoItem + List | v-for, TransitionGroup, scoped slots, emits, custom rendering |
| Tabbed interface with KeepAlive | KeepAlive, activated/deactivated, state preservation, scroll restore |
| ErrorBoundary | onErrorCaptured, error handling, fallback UI |
| Chat with auto-scroll | onUpdated + nextTick, scroll restoration, v-for |
| Form with validation & multiple v-model | v-model, .trim/.number/.lazy, computed validation, emits |
Pro Tips from Real Projects (Hyderabad 2026)
- Start with <script setup> — all examples above use it (no Options API)
- Always use :key in v-for — especially with <TransitionGroup>
- Always declare emits with defineEmits<…>() — TypeScript + autocompletion + typo protection
- Use defineExpose only when parent really needs to call child methods (modal open/close, form validate…)
- Prefer events over expose for most parent-child communication — cleaner architecture
- Use <KeepAlive> + onActivated/onDeactivated for tabs/router-view — preserves state, scroll, polling, video playback
- Wrap risky parts in <ErrorBoundary> — prevents full app crash from one bad child
- Use Teleport for modals/tooltips/notifications — always append to <body>
Your mini project challenge (build this weekend):
- Create a small dashboard with:
- <KeepAlive> + tabs (overview, settings, profile)
- Modal with v-model + expose + slots
- Todo list with <TransitionGroup> + scoped slots
- ErrorBoundary around a “risky” chart component
- Add form in profile tab → verify state preserved on tab switch
- Add scrollable chat in one tab → verify scroll restoration
Any part confusing? Want full code for:
- Complete dashboard example with all pieces combined?
- Modal + form + validation + v-model multiple?
- Chat with infinite scroll + auto-load + onActivated restore?
- ErrorBoundary + Sentry integration?
Just tell me — we’ll build the next beautiful, real-world Vue component together 🚀
