-
-
Notifications
You must be signed in to change notification settings - Fork 472
Description
Environment
Package Version:
- reka-ui: ^2.7.0
- vue: ^3.5.13
- vee-validate: ^4.15.1
- nuxt: ^3.13.0
Browser:
- Chrome (latest)
- macOS
Node Version:
- v24.9.0Link to minimal reproduction
Note: We cannot provide a public repository link due to company policy, but we can provide detailed reproduction steps and code examples below.
Steps to reproduce
- Create a Vue 3 form component with multiple SelectField components using SelectPortal from reka-ui
- Wrap the form with vee-validate's Form component
- Fill the form and submit it multiple times (5-10 times)
- Open Chrome DevTools → Memory tab
- Take a heap snapshot before submitting the form
- Submit the form 5-10 times
- Take another heap snapshot after submissions
- Compare the snapshots and filter for "Detached" elements
Expected Result:
- Memory usage should remain stable or increase minimally (1-2 MB for 5 submissions)
- Detached DOM nodes should be cleaned up automatically
- Detached EventListeners should be 0 or decreasing
Actual Result:
- Memory usage increases significantly (+46 MB for 5 submissions)
- Detached DOM nodes accumulate (Detached Text: +976, Detached : +211)
- Detached EventListeners accumulate (+497)
Describe the bug
The SelectPortal component from reka-ui is causing a serious memory leak by not properly cleaning up DOM nodes and event listeners when select components are closed or unmounted.
Problem Details
When using SelectPortal within a form that gets submitted multiple times, portal containers and their child elements remain in memory as detached DOM nodes. This causes:
- Memory usage to increase significantly with each form submission
- Detached DOM nodes to accumulate (Text nodes, Div elements)
- Event listeners to not be cleaned up, causing them to accumulate in memory
- Browser performance degradation over time
- Potential browser crashes on low-memory devices
Evidence
Test Results:
Test 1: Form without reka-ui (TextField only)
- 20 form submissions
- Memory increase: +3 MB ✅ (Normal)
- Detached EventListeners: -12 (decreasing) ✅
- Detached Text nodes: -12 (decreasing) ✅
Test 2: Form with reka-ui SelectPortal
- 5 form submissions
- Memory increase: +46 MB ❌ (23-46x more than normal)
- Detached EventListeners: +497 ❌ (should be 0 or decreasing)
- Detached Text nodes: +976 ❌ (should be ±10)
- Detached elements: +211 ❌ (should be ±5)
Chrome DevTools Analysis:
After 5 form submissions:
- Heap size: Increased from ~107 MB to ~153 MB (+46 MB)
- Detached EventListeners: +497 (critical - these should be cleaned up)
- Detached Text nodes: +976 (very high - normal is ±10)
- Detached elements: +211 (very high - normal is ±5)
Code Example
Germany USA Submit <script setup> import { Form } from 'vee-validate'; import { SelectRoot, SelectTrigger, SelectValue, SelectPortal, SelectContent } from 'reka-ui'; const handleSubmit = () => { // After multiple submissions, memory leak occurs }; </script>SelectField Component Implementation:
<script setup> import { SelectRoot, SelectTrigger, SelectValue, SelectPortal, SelectContent } from 'reka-ui'; import { onBeforeUnmount } from 'vue'; const onUpdateOpen = (open: boolean) => { // When select closes, portal containers should be cleaned up // But they remain in DOM as detached nodes }; onBeforeUnmount(() => { // Manual cleanup attempt - but portal containers still remain // This confirms the issue is in reka-ui's SelectPortal }); </script>Root Cause Analysis
The SelectPortal component creates portal elements that are appended to document.body. When the select component is:
- Closed (@update:open with false)
- Unmounted (onBeforeUnmount)
- Form is reset or re-rendered
These portal containers are not properly removed from the DOM, leaving detached DOM nodes in memory. Additionally, event listeners attached to these portal elements are not disposed of, causing them to accumulate.
Impact
This issue is critical for applications with forms that are submitted multiple times, as it can lead to:
- Browser performance degradation
- Potential browser crashes on low-memory devices
- Poor user experience
- Production stability issues
Workaround Attempts
We've tried several workarounds without success:
- ✅ Added onBeforeUnmount hook to manually remove portal containers - Partial success (~85% improvement) but still residual leak
- ✅ Added cleanup on select close event (@update:open) - Still residual leak
- ✅ Updated to [email protected] - Issue persists
Suggested Fix
The SelectPortal component should:
- Clean up portal containers when the select is closed (@update:open with false)
- Remove portal containers from DOM on component unmount (onBeforeUnmount)
- Properly dispose of event listeners attached to portal elements
- Ensure garbage collection can reclaim memory from detached nodes
Additional Context
- This issue is specific to SelectPortal - other reka-ui components (Toast, Tabs, Accordion) work correctly
- We've confirmed the issue is not from vee-validate by testing with TextField components (no memory leak)
- The issue is reproducible and consistent across multiple form submissions
Expected behavior
No response
Context & Screenshots (if applicable)
No response