vuejs_dialogs_modals 36 Q&As

Vue.js Dialogs Modals FAQ & Answers

36 expert Vue.js Dialogs Modals answers researched from official documentation. Every answer cites authoritative sources you can verify.

unknown

36 questions
A

Use native HTML

element with Vue 3 refs for modern, accessible modals. Implementation: create reusable Modal.vue component with and expose showModal() method via defineExpose(). Example: . Parent usage: Content then modal.value.open(). Benefits: built-in browser features (backdrop, Escape key handling, focus management), better accessibility than div-based modals. For complex use cases, use promise-based libraries like vue3-promise-dialog: const result = await openDialog(ConfirmDialog, { message: 'Delete?' }). Avoid native alert() because: blocks UI thread, not customizable, poor UX, no async/await support. Production pattern: combine for simple modals + promise-based dialogs for async workflows.

99% confidence
A

Use promise-based dialog pattern for reusable, async-friendly confirmations. Pattern: dialog function returns Promise that resolves with user choice. Implementation with vue3-promise-dialog: Create ConfirmDialog.vue with props (message, confirmText, cancelText) and two buttons that call close(true) or close(false). Register: app.use(PromiseDialog). Usage: const confirmed = await openDialog(ConfirmDialog, { message: 'Delete item?' }); if (confirmed) { deleteItem(); }. Benefits: (1) Call from any JS/TS file (not just components), (2) Async/await syntax (cleaner than callbacks), (3) TypeScript type safety for props and return values, (4) Single dialog instance reused across app. Alternative: create global dialog service with provide/inject, but promise pattern is more intuitive. Renderless design: separate logic (promise handling) from presentation (styling) for maximum reusability across different UI frameworks.

99% confidence
A

Use vue3-promise-dialog library for type-safe promise-based dialogs. Setup: npm install vue3-promise-dialog, then in main.ts: import { PromiseDialog } from 'vue3-promise-dialog'; app.use(PromiseDialog);. Create dialog component with TypeScript props interface: interface Props { title: string; message: string; }; const props = defineProps();. Import openDialog function with type parameter: import { openDialog } from 'vue3-promise-dialog'; const result = await openDialog(ConfirmDialog, { title: 'Confirm', message: 'Delete?' }); if (result) { /* user confirmed */ }. TypeScript benefits: (1) Props auto-completed and type-checked, (2) Return type enforced (Promise), (3) Compile-time errors for missing props. Close dialog with typed result: const close = inject<(result: boolean) => void>('close'); close(true);. Alternative: vue-promise-dialogs library supports Vue 2 and 3 in single package, similar API.

99% confidence
A

Custom dialogs provide superior UX and functionality over native alert(). Key benefits: (1) Non-blocking: native alert() blocks JavaScript execution and UI rendering, custom dialogs use async/await without blocking, (2) Customizable: full control over styling, branding, animations, layout vs fixed browser chrome, (3) Accessibility: add ARIA attributes, focus management, keyboard navigation, screen reader support, (4) Rich content: include forms, images, complex HTML vs plain text only, (5) Consistent UX: same look across all browsers/OS vs native browser styling, (6) Better mobile experience: responsive design, touch-friendly buttons vs tiny native buttons, (7) Testable: easy to unit test custom components vs difficulty testing native alerts. Technical advantages: promise-based API (cleaner code), can programmatically close, support multiple simultaneous dialogs, integrate with state management (Pinia/Vuex). Native alert() only acceptable for quick debugging, never production code.

99% confidence
A

Implement dialog queue system to prevent multiple overlapping modals. Strategy 1: Single dialog instance with queue. Use reactive queue: const dialogQueue = ref<DialogConfig[]>([]); const currentDialog = computed(() => dialogQueue.value[0]); function openDialog(config) { dialogQueue.value.push(config); } function closeDialog() { dialogQueue.value.shift(); }. Only render when currentDialog exists. Strategy 2: Disable trigger buttons when dialog open. Track open state: const isDialogOpen = ref(false);

99% confidence
A

Accessible confirmation dialogs must support standard keyboard shortcuts per WCAG 2.1 guidelines. Required shortcuts: (1) Escape key: close dialog (most critical), (2) Tab/Shift+Tab: cycle focus within dialog (focus trap), (3) Enter key: activate focused button (typically 'Confirm'), (4) Space key: activate focused button. Implementation with Vue 3 Composition API: onMounted(() => { window.addEventListener('keydown', handleKeydown); }); onUnmounted(() => { window.removeEventListener('keydown', handleKeydown); }); const handleKeydown = (e: KeyboardEvent) => { if (e.key === 'Escape') close(false); if (e.key === 'Enter' && focusedElement === confirmButton) confirm(); }. Best practices: (1) Focus 'Cancel' button by default (safer, prevents accidental confirmation), (2) Highlight focused button visibly (outline, background color), (3) Arrow keys for button navigation (left/right). Avoid: requiring mouse for any action, keyboard traps user cannot escape, no visible focus indicators.

99% confidence
A

Props and slots serve different purposes in modal components. Props: pass configuration data (title, size, closable, variant), typically simple types (string, boolean, number). Example: . Use props for: behavior flags, styling variants, static content. Slots: pass complex HTML content, child components, dynamic rendering. Example: <template #header>Custom Header<template #body>. Named slots provide flexibility: header, body, footer, actions slots for different modal sections. Default slot: simplest use case Simple content. Best practice: combine both - props for configuration, slots for content: <template #actions><button @click="confirm">Delete. Scoped slots: pass data from modal to parent: <template #footer="{ close }"><button @click="close">Done. Props = what the modal does, Slots = what the modal shows. This separation makes modals highly reusable.

99% confidence
A

Teleport renders component content outside current DOM hierarchy, essential for modals to avoid CSS stacking issues. Basic implementation:

. The to attribute accepts CSS selector (to="body", to="#app", to=".modal-container"). Why needed: modals defined deep in component tree inherit parent CSS (overflow: hidden, z-index stacking contexts), Teleport moves modal to document body, escaping these constraints. Vue 3.5+ features: (1) defer prop: waits for target to mount (useful for dynamic containers), (2) disabled prop: conditionally disable (render inline on mobile, teleport on desktop). Multiple teleports to same target: order preserved via append. Combine with Transition:
...
for animated modals. Always teleport modals to avoid z-index hell.

99% confidence
A

Z-index stacking context determines layering order of positioned elements. New stacking contexts created by: position (absolute/relative/fixed) + z-index, transform, opacity < 1, filter, will-change. Problem: z-index only works within same stacking context, child with z-index: 9999 cannot appear above parent's sibling with z-index: 2. Modal solution: use Teleport to move modal outside parent stacking contexts:

. Z-index strategy: define CSS variables: --z-dropdown: 100; --z-sticky: 200; --z-modal: 1000; --z-tooltip: 2000; --z-notification: 3000. Avoid random high values (z-index: 999999), causes maintenance nightmare. Debug stacking: browser DevTools shows stacking context hierarchy. Best practice: (1) Minimize positioned elements, (2) Use Teleport for overlays, (3) Document z-index scale in CSS comments, (4) Use CSS custom properties for consistency. If modal appears below content, check parent transform/opacity creating new stacking context.

99% confidence
A

Focus trap keeps keyboard navigation within modal, essential for accessibility. Solution: use focus-trap library with VueUse's useFocusTrap composable. Installation: npm install focus-trap. Implementation: import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'; const modalRef = ref(); const { activate, deactivate } = useFocusTrap(modalRef); onMounted(() => activate()); onUnmounted(() => deactivate());. How it works: (1) Finds all focusable elements (buttons, inputs, links) within modal, (2) Tab from last element returns to first, (3) Shift+Tab from first returns to last, (4) Escape key deactivates trap. Manual implementation: const focusableSelector = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'; const focusable = modalRef.value.querySelectorAll(focusableSelector); document.addEventListener('keydown', (e) => { if (e.key === 'Tab') { /* cycle through focusable */ } });. Best practices: (1) Focus first interactive element on open (close button or primary action), (2) Return focus to trigger element on close, (3) Use aria-modal="true" to tell screen readers only modal is accessible.

99% confidence
A

Accessible modals require specific ARIA attributes per WCAG 2.1 guidelines. Required attributes: (1) role="dialog" or role="alertdialog" on modal container (alertdialog for critical confirmations), (2) aria-modal="true" tells screen readers only modal content is accessible (hides background), (3) aria-labelledby="title-id" references modal title element ID, (4) aria-describedby="desc-id" references description/content element ID. Example:

. Additional attributes: (5) aria-hidden="true" on background content when modal open (prevents screen reader access), (6) tabindex="-1" on modal container for programmatic focus. Implementation: when modal opens, set document.body.setAttribute('aria-hidden', 'true'), remove on close. Use Vue directive: v-bind:aria-modal="isOpen". Screen reader announces: 'Dialog: Delete User' followed by description. Test with NVDA/JAWS screen readers.

99% confidence
A

Backdrop click should close modal but requires careful event handling to prevent bugs. Pattern: listen for click on overlay (backdrop), not modal content. Implementation: <div class="overlay" @click="handleOverlayClick"><div class="modal" @click.stop>Content

. The @click.stop prevents event bubbling from modal content to overlay. Safer pattern using self modifier: <div class="overlay" @click.self="close">. Clicks on overlay close modal, clicks on modal children do nothing. Accessibility considerations: (1) Provide visible close button (don't rely only on backdrop click), (2) Make backdrop click optional: :clickOutsideToClose="true" prop, (3) Disable for critical confirmations (force explicit choice). Advanced: detect click start and end on overlay: let clickStartedOnOverlay = false; @mousedown="clickStartedOnOverlay = true" @mouseup="if (clickStartedOnOverlay) close()". This prevents accidental closes when user selects text in modal and releases mouse outside. Best practice: combine Escape key + close button + optional backdrop click for maximum usability.

99% confidence
A

Use Vue 3 Composition API with onMounted/onUnmounted for proper event listener cleanup. Pattern: onMounted(() => { window.addEventListener('keydown', handleKeydown); }); onUnmounted(() => { window.removeEventListener('keydown', handleKeydown); }); const handleKeydown = (e: KeyboardEvent) => { if (e.key === 'Escape') close(); }. Key modifiers in templates: @keydown.enter="submit", @keydown.esc="close", @keydown.ctrl.s.prevent="save" (Ctrl+S). System modifiers: .ctrl, .alt, .shift, .meta (Cmd on Mac). Exact modifier: @keydown.ctrl.exact prevents trigger when other keys pressed. VueUse composables: import { onKeyStroke } from '@vueuse/core'; onKeyStroke('Escape', () => close()); automatically handles cleanup. Global vs component listeners: window.addEventListener for app-wide shortcuts, @keydown on specific elements for component-scoped. Performance: use event.key (modern) not event.keyCode (deprecated). Prevent default: e.preventDefault() stops browser default behavior. Best practice: always remove listeners in onUnmounted to prevent memory leaks.

99% confidence
A

onMounted and onUnmounted are Composition API lifecycle hooks for setup and cleanup. onMounted: called after component is inserted into DOM, perfect for: (1) DOM manipulation (element dimensions, focus management), (2) Initialize third-party libraries (charts, maps), (3) Add event listeners, (4) Fetch initial data, (5) Start timers/intervals. Example: onMounted(() => { inputRef.value.focus(); api.fetchData(); }). Runs once per component instance. onUnmounted: called before component is removed from DOM, critical for cleanup to prevent memory leaks: (1) Remove event listeners, (2) Clear timers/intervals, (3) Cancel pending requests, (4) Destroy third-party instances. Example: onUnmounted(() => { window.removeEventListener('resize', handler); clearInterval(intervalId); controller.abort(); }). Execution order: setup() → onMounted() → ... → onUnmounted(). Comparison to Options API: onMounted ≈ mounted(), onUnmounted ≈ beforeUnmount(). Best practice: every setup action in onMounted should have corresponding cleanup in onUnmounted. Use VueUse composables (onKeyStroke, useEventListener) for automatic cleanup.

99% confidence
A

Always remove event listeners in onUnmounted hook to prevent memory leaks. Leak pattern: onMounted(() => { window.addEventListener('resize', handleResize); }); // LEAK! No cleanup. Fixed pattern: const handleResize = () => { /* logic */ }; onMounted(() => { window.addEventListener('resize', handleResize); }); onUnmounted(() => { window.removeEventListener('resize', handleResize); }); // Cleanup. Important: use same function reference in add/remove (avoid anonymous functions). Best solution: VueUse's useEventListener: import { useEventListener } from '@vueuse/core'; useEventListener(window, 'resize', handleResize); // Auto cleanup! Other leak sources: (1) Intervals: const id = setInterval(() => {}, 1000); onUnmounted(() => clearInterval(id)); (2) Timeouts: const id = setTimeout(() => {}, 1000); onUnmounted(() => clearTimeout(id)); (3) Third-party libraries: onUnmounted(() => { chart.destroy(); map.remove(); }). Detect leaks: Chrome DevTools → Memory → Take heap snapshot before/after component mount/unmount, check for detached DOM nodes. Production best practice: use VueUse composables which handle cleanup automatically.

99% confidence
A

Use . Parent usage: Content then modal.value.open(). Benefits: built-in browser features (backdrop, Escape key handling, focus management), better accessibility than div-based modals. For complex use cases, use promise-based libraries like vue3-promise-dialog: const result = await openDialog(ConfirmDialog, { message: 'Delete?' }). Avoid native alert() because: blocks UI thread, not customizable, poor UX, no async/await support. Production pattern: combine

for simple modals + promise-based dialogs for async workflows.

99% confidence
A

Use promise-based dialog pattern for reusable, async-friendly confirmations. Pattern: dialog function returns Promise that resolves with user choice. Implementation with vue3-promise-dialog: Create ConfirmDialog.vue with props (message, confirmText, cancelText) and two buttons that call close(true) or close(false). Register: app.use(PromiseDialog). Usage: const confirmed = await openDialog(ConfirmDialog, { message: 'Delete item?' }); if (confirmed) { deleteItem(); }. Benefits: (1) Call from any JS/TS file (not just components), (2) Async/await syntax (cleaner than callbacks), (3) TypeScript type safety for props and return values, (4) Single dialog instance reused across app. Alternative: create global dialog service with provide/inject, but promise pattern is more intuitive. Renderless design: separate logic (promise handling) from presentation (styling) for maximum reusability across different UI frameworks.

99% confidence
A

Use vue3-promise-dialog library for type-safe promise-based dialogs. Setup: npm install vue3-promise-dialog, then in main.ts: import { PromiseDialog } from 'vue3-promise-dialog'; app.use(PromiseDialog);. Create dialog component with TypeScript props interface: interface Props { title: string; message: string; }; const props = defineProps();. Import openDialog function with type parameter: import { openDialog } from 'vue3-promise-dialog'; const result = await openDialog(ConfirmDialog, { title: 'Confirm', message: 'Delete?' }); if (result) { /* user confirmed */ }. TypeScript benefits: (1) Props auto-completed and type-checked, (2) Return type enforced (Promise), (3) Compile-time errors for missing props. Close dialog with typed result: const close = inject<(result: boolean) => void>('close'); close(true);. Alternative: vue-promise-dialogs library supports Vue 2 and 3 in single package, similar API.

99% confidence
A

Custom dialogs provide superior UX and functionality over native alert(). Key benefits: (1) Non-blocking: native alert() blocks JavaScript execution and UI rendering, custom dialogs use async/await without blocking, (2) Customizable: full control over styling, branding, animations, layout vs fixed browser chrome, (3) Accessibility: add ARIA attributes, focus management, keyboard navigation, screen reader support, (4) Rich content: include forms, images, complex HTML vs plain text only, (5) Consistent UX: same look across all browsers/OS vs native browser styling, (6) Better mobile experience: responsive design, touch-friendly buttons vs tiny native buttons, (7) Testable: easy to unit test custom components vs difficulty testing native alerts. Technical advantages: promise-based API (cleaner code), can programmatically close, support multiple simultaneous dialogs, integrate with state management (Pinia/Vuex). Native alert() only acceptable for quick debugging, never production code.

99% confidence
A

Implement dialog queue system to prevent multiple overlapping modals. Strategy 1: Single dialog instance with queue. Use reactive queue: const dialogQueue = ref<DialogConfig[]>([]); const currentDialog = computed(() => dialogQueue.value[0]); function openDialog(config) { dialogQueue.value.push(config); } function closeDialog() { dialogQueue.value.shift(); }. Only render when currentDialog exists. Strategy 2: Disable trigger buttons when dialog open. Track open state: const isDialogOpen = ref(false);

99% confidence
A

Accessible confirmation dialogs must support standard keyboard shortcuts per WCAG 2.1 guidelines. Required shortcuts: (1) Escape key: close dialog (most critical), (2) Tab/Shift+Tab: cycle focus within dialog (focus trap), (3) Enter key: activate focused button (typically 'Confirm'), (4) Space key: activate focused button. Implementation with Vue 3 Composition API: onMounted(() => { window.addEventListener('keydown', handleKeydown); }); onUnmounted(() => { window.removeEventListener('keydown', handleKeydown); }); const handleKeydown = (e: KeyboardEvent) => { if (e.key === 'Escape') close(false); if (e.key === 'Enter' && focusedElement === confirmButton) confirm(); }. Best practices: (1) Focus 'Cancel' button by default (safer, prevents accidental confirmation), (2) Highlight focused button visibly (outline, background color), (3) Arrow keys for button navigation (left/right). Avoid: requiring mouse for any action, keyboard traps user cannot escape, no visible focus indicators.

99% confidence
A

Props and slots serve different purposes in modal components. Props: pass configuration data (title, size, closable, variant), typically simple types (string, boolean, number). Example: . Use props for: behavior flags, styling variants, static content. Slots: pass complex HTML content, child components, dynamic rendering. Example: <template #header>Custom Header<template #body>. Named slots provide flexibility: header, body, footer, actions slots for different modal sections. Default slot: simplest use case Simple content. Best practice: combine both - props for configuration, slots for content: <template #actions><button @click="confirm">Delete. Scoped slots: pass data from modal to parent: <template #footer="{ close }"><button @click="close">Done. Props = what the modal does, Slots = what the modal shows. This separation makes modals highly reusable.

99% confidence
A

Teleport renders component content outside current DOM hierarchy, essential for modals to avoid CSS stacking issues. Basic implementation:

. The to attribute accepts CSS selector (to="body", to="#app", to=".modal-container"). Why needed: modals defined deep in component tree inherit parent CSS (overflow: hidden, z-index stacking contexts), Teleport moves modal to document body, escaping these constraints. Vue 3.5+ features: (1) defer prop: waits for target to mount (useful for dynamic containers), (2) disabled prop: conditionally disable (render inline on mobile, teleport on desktop). Multiple teleports to same target: order preserved via append. Combine with Transition:
...
for animated modals. Always teleport modals to avoid z-index hell.

99% confidence
A

Z-index stacking context determines layering order of positioned elements. New stacking contexts created by: position (absolute/relative/fixed) + z-index, transform, opacity < 1, filter, will-change. Problem: z-index only works within same stacking context, child with z-index: 9999 cannot appear above parent's sibling with z-index: 2. Modal solution: use Teleport to move modal outside parent stacking contexts:

. Z-index strategy: define CSS variables: --z-dropdown: 100; --z-sticky: 200; --z-modal: 1000; --z-tooltip: 2000; --z-notification: 3000. Avoid random high values (z-index: 999999), causes maintenance nightmare. Debug stacking: browser DevTools shows stacking context hierarchy. Best practice: (1) Minimize positioned elements, (2) Use Teleport for overlays, (3) Document z-index scale in CSS comments, (4) Use CSS custom properties for consistency. If modal appears below content, check parent transform/opacity creating new stacking context.

99% confidence
A

Focus trap keeps keyboard navigation within modal, essential for accessibility. Solution: use focus-trap library with VueUse's useFocusTrap composable. Installation: npm install focus-trap. Implementation: import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'; const modalRef = ref(); const { activate, deactivate } = useFocusTrap(modalRef); onMounted(() => activate()); onUnmounted(() => deactivate());. How it works: (1) Finds all focusable elements (buttons, inputs, links) within modal, (2) Tab from last element returns to first, (3) Shift+Tab from first returns to last, (4) Escape key deactivates trap. Manual implementation: const focusableSelector = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'; const focusable = modalRef.value.querySelectorAll(focusableSelector); document.addEventListener('keydown', (e) => { if (e.key === 'Tab') { /* cycle through focusable */ } });. Best practices: (1) Focus first interactive element on open (close button or primary action), (2) Return focus to trigger element on close, (3) Use aria-modal="true" to tell screen readers only modal is accessible.

99% confidence
A

Accessible modals require specific ARIA attributes per WCAG 2.1 guidelines. Required attributes: (1) role="dialog" or role="alertdialog" on modal container (alertdialog for critical confirmations), (2) aria-modal="true" tells screen readers only modal content is accessible (hides background), (3) aria-labelledby="title-id" references modal title element ID, (4) aria-describedby="desc-id" references description/content element ID. Example:

. Additional attributes: (5) aria-hidden="true" on background content when modal open (prevents screen reader access), (6) tabindex="-1" on modal container for programmatic focus. Implementation: when modal opens, set document.body.setAttribute('aria-hidden', 'true'), remove on close. Use Vue directive: v-bind:aria-modal="isOpen". Screen reader announces: 'Dialog: Delete User' followed by description. Test with NVDA/JAWS screen readers.

99% confidence
A

Backdrop click should close modal but requires careful event handling to prevent bugs. Pattern: listen for click on overlay (backdrop), not modal content. Implementation: <div class="overlay" @click="handleOverlayClick"><div class="modal" @click.stop>Content

. The @click.stop prevents event bubbling from modal content to overlay. Safer pattern using self modifier: <div class="overlay" @click.self="close">. Clicks on overlay close modal, clicks on modal children do nothing. Accessibility considerations: (1) Provide visible close button (don't rely only on backdrop click), (2) Make backdrop click optional: :clickOutsideToClose="true" prop, (3) Disable for critical confirmations (force explicit choice). Advanced: detect click start and end on overlay: let clickStartedOnOverlay = false; @mousedown="clickStartedOnOverlay = true" @mouseup="if (clickStartedOnOverlay) close()". This prevents accidental closes when user selects text in modal and releases mouse outside. Best practice: combine Escape key + close button + optional backdrop click for maximum usability.

99% confidence
A

Use Vue 3 Composition API with onMounted/onUnmounted for proper event listener cleanup. Pattern: onMounted(() => { window.addEventListener('keydown', handleKeydown); }); onUnmounted(() => { window.removeEventListener('keydown', handleKeydown); }); const handleKeydown = (e: KeyboardEvent) => { if (e.key === 'Escape') close(); }. Key modifiers in templates: @keydown.enter="submit", @keydown.esc="close", @keydown.ctrl.s.prevent="save" (Ctrl+S). System modifiers: .ctrl, .alt, .shift, .meta (Cmd on Mac). Exact modifier: @keydown.ctrl.exact prevents trigger when other keys pressed. VueUse composables: import { onKeyStroke } from '@vueuse/core'; onKeyStroke('Escape', () => close()); automatically handles cleanup. Global vs component listeners: window.addEventListener for app-wide shortcuts, @keydown on specific elements for component-scoped. Performance: use event.key (modern) not event.keyCode (deprecated). Prevent default: e.preventDefault() stops browser default behavior. Best practice: always remove listeners in onUnmounted to prevent memory leaks.

99% confidence
A

onMounted and onUnmounted are Composition API lifecycle hooks for setup and cleanup. onMounted: called after component is inserted into DOM, perfect for: (1) DOM manipulation (element dimensions, focus management), (2) Initialize third-party libraries (charts, maps), (3) Add event listeners, (4) Fetch initial data, (5) Start timers/intervals. Example: onMounted(() => { inputRef.value.focus(); api.fetchData(); }). Runs once per component instance. onUnmounted: called before component is removed from DOM, critical for cleanup to prevent memory leaks: (1) Remove event listeners, (2) Clear timers/intervals, (3) Cancel pending requests, (4) Destroy third-party instances. Example: onUnmounted(() => { window.removeEventListener('resize', handler); clearInterval(intervalId); controller.abort(); }). Execution order: setup() → onMounted() → ... → onUnmounted(). Comparison to Options API: onMounted ≈ mounted(), onUnmounted ≈ beforeUnmount(). Best practice: every setup action in onMounted should have corresponding cleanup in onUnmounted. Use VueUse composables (onKeyStroke, useEventListener) for automatic cleanup.

99% confidence
A

Always remove event listeners in onUnmounted hook to prevent memory leaks. Leak pattern: onMounted(() => { window.addEventListener('resize', handleResize); }); // LEAK! No cleanup. Fixed pattern: const handleResize = () => { /* logic */ }; onMounted(() => { window.addEventListener('resize', handleResize); }); onUnmounted(() => { window.removeEventListener('resize', handleResize); }); // Cleanup. Important: use same function reference in add/remove (avoid anonymous functions). Best solution: VueUse's useEventListener: import { useEventListener } from '@vueuse/core'; useEventListener(window, 'resize', handleResize); // Auto cleanup! Other leak sources: (1) Intervals: const id = setInterval(() => {}, 1000); onUnmounted(() => clearInterval(id)); (2) Timeouts: const id = setTimeout(() => {}, 1000); onUnmounted(() => clearTimeout(id)); (3) Third-party libraries: onUnmounted(() => { chart.destroy(); map.remove(); }). Detect leaks: Chrome DevTools → Memory → Take heap snapshot before/after component mount/unmount, check for detached DOM nodes. Production best practice: use VueUse composables which handle cleanup automatically.

99% confidence
A

Use