# DOM Widget Value Persistence - Best Practices ## Problem DOM widgets with text inputs failed to persist values after: - Loading workflows - Switching workflows - Reloading pages ## Root Cause **Multiple sources of truth** causing sync issues: - Internal state variable (`internalValue` in main.ts) - Vue reactive ref (`textValue` in component) - DOM element value (actual textarea) - ComfyUI widget value (`props.widget.value`) **Broken sync chains:** ``` getValue() → internalValue (not actual DOM value) setValue(v) → internalValue → onSetValue() → textValue.value (async chain) serializeValue() → textValue.value (different from getValue) watch() → another sync layer ``` ## Solution Follow ComfyUI built-in `addMultilineWidget` pattern: ### ✅ Do 1. **Single source of truth**: Use the DOM element directly ```typescript // main.ts const widget = node.addDOMWidget(name, type, container, { getValue() { return widget.inputEl?.value ?? '' }, setValue(v: string) { if (widget.inputEl) { widget.inputEl.value = v ?? '' } } }) ``` 2. **Register DOM reference** when component mounts ```typescript // Vue component onMounted(() => { if (textareaRef.value) { props.widget.inputEl = textareaRef.value } }) ``` 3. **Clean up reference** on unmount ```typescript onUnmounted(() => { if (props.widget.inputEl === textareaRef.value) { props.widget.inputEl = undefined } }) ``` 4. **Simplify interface** - only expose what's needed ```typescript export interface MyWidgetInterface { inputEl?: HTMLTextAreaElement callback?: (v: string) => void } ``` ### ❌ Don't 1. **Don't create internal state variables** ```typescript // Wrong let internalValue = '' getValue() { return internalValue } ``` 2. **Don't use v-model** for text inputs in DOM widgets ```html