Chapter 61: Vue $parent Object
The $parent object (also written as this.$parent in Options API)
This is not something you should reach for often in modern Vue 3 code — but understanding it properly will help you read legacy projects, debug strange behavior, and most importantly: know when NOT to use it.
I’m going to explain it honestly, like a senior dev sitting next to you saying: “Here’s what it is, here’s why it feels tempting, and here’s why you should almost never touch it in 2026.”
1. What exactly is $parent?
$parent is a direct reference to the parent component instance of the current component.
In other words:
- Every component (except the root App) has exactly one parent
- $parent gives you the live instance of that parent
- You can read/write its data, call its methods, emit events upward through it, access its $refs, $children, etc.
Vue 2 / Options API style (still very common in legacy code)
|
0 1 2 3 4 5 6 7 8 |
this.$parent.someData = 42 this.$parent.$refs.myInput.focus() this.$parent.$emit('custom-event') |
Vue 3 / Composition API style (you almost never do this anymore)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<script setup> import { getCurrentInstance } from 'vue' const instance = getCurrentInstance() const parent = instance?.parent // parent is the parent component instance (or null) parent?.exposed?.someMethod?.() </script> |
2. Why does $parent exist? (The temptation)
It feels like a quick fix when:
- You need to close a modal from inside a deeply nested button
- You want to update parent state without emitting an event up multiple levels
- You need to access a parent ref (e.g. focus an input in parent form)
- You’re building a reusable modal / dialog / dropdown and want to tell parent “I’m done”
- You’re in a rush / hackathon and just want it to work
But here’s the harsh truth (2026 senior dev voice):
Every time you use $parent, you are creating tight coupling, fragile code, and future debugging pain.
3. Real Example – The Classic “Bad Way” (Do NOT copy this)
Child: ModalContent.vue (deep inside some form)
|
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 |
<template> <div class="modal-content"> <h2>Confirm Delete</h2> <button @click="closeModal">Cancel</button> <button @click="confirmDelete">Yes, Delete</button> </div> </template> <script setup> // BAD: using $parent to reach up multiple levels const instance = getCurrentInstance() const parent = instance?.parent?.parent?.parent // fragile! function closeModal() { parent?.exposed?.close?.() // assuming parent exposes close() } function confirmDelete() { parent?.exposed?.deleteItem?.() closeModal() } </script> |
Problems with this approach
- Breaks if you move ModalContent one level deeper/shallower → parent.parent.parent becomes wrong
- Breaks if parent doesn’t expose close / deleteItem
- Hard to test (parent instance is not mockable easily)
- No type safety (TypeScript can’t help)
- Creates hidden dependencies — new developers won’t know why modal closes
4. The Right Ways (What you should always do in 2026)
Correct Way #1 – Emit Events (Clean & Recommended)
Child emits → parent listens → parent handles state
Child
|
0 1 2 3 4 5 6 7 8 9 |
const emit = defineEmits(['close', 'confirm']) function close() { emit('close') } function confirm() { emit('confirm') } |
Parent
|
0 1 2 3 4 5 6 |
<ModalContent @close="showModal = false" @confirm="deleteItem()" /> |
→ Clean, unidirectional, testable, scalable
Correct Way #2 – v-model / update:prop Pattern
For two-way data (very common for modals)
Child
|
0 1 2 3 4 5 6 7 8 9 10 |
defineEmits(['update:modelValue']) function close() { emit('update:modelValue', false) } |
Parent
|
0 1 2 3 4 5 6 |
<Modal v-model="showModal" /> |
Correct Way #3 – Provide / Inject (for deep nesting without prop drilling)
Ancestor provides close function → any descendant injects it
Ancestor (App.vue or Layout)
|
0 1 2 3 4 5 6 |
provide('closeModal', () => showModal.value = false) |
Deep child
|
0 1 2 3 4 5 6 7 |
const closeModal = inject('closeModal') closeModal() |
→ No prop chains, no $parent
Quick Summary Table – $parent vs Modern Alternatives
| Situation | Using $parent (bad) | Modern & Recommended (2026) | Why modern is better |
|---|---|---|---|
| Close modal from deep child | this.$parent.$parent.close() | @close emit or inject(‘closeModal’) | Decoupled, testable |
| Update parent state | this.$parent.someData = newVal | Emit ‘update:someData’ | Unidirectional flow |
| Access parent ref | this.$parent.$refs.input.focus() | Emit event → parent focuses | No tight coupling |
| Deeply nested auth check | this.$parent.$parent.isAdmin | inject(‘auth’) or Pinia store | Clean, type-safe |
| Legacy code smell indicator | Lots of this.$parent | None | — |
Final 2026 Advice from Real Projects
- Never use $parent in new code — it’s a code smell
- When you see $parent → it usually means:
- Old Vue 2 / early Vue 3 code
- Quick hack / prototype
- Developer didn’t know about emits / provide-inject / Pinia
- Prefer (in this order):
- Events (emit) — most common & clean
- Provide / Inject — for deep shared read-only data
- Pinia — for global or shared state
- Context object via provide (advanced)
- Only use $parent when:
- Debugging legacy code
- Writing a temporary hack you plan to refactor
- Integrating very old third-party Vue 2 components
Your mini homework:
- Create a modal component deep inside a form → try to close it using $parent (see how fragile it is)
- Refactor it using emit(‘close’) → clean & reliable
- Refactor again using provide/inject → no prop drilling
Any part confusing? Want to see:
- Full modal with emit + provide/inject comparison?
- Real legacy code using $parent → how to refactor it?
- $parent vs $root vs getCurrentInstance().parent?
- When $parent is actually okay (rare edge cases)?
Just tell me — we’ll refactor and clean up the next component together 🚀
