[{"data":1,"prerenderedAt":3196},["ShallowReactive",2],{"page-\u002Fvue\u002F06-component-communication":3},{"id":4,"title":5,"body":6,"description":3189,"extension":3190,"meta":3191,"navigation":97,"path":3192,"seo":3193,"stem":3194,"__hash__":3195},"content\u002Fvue\u002F06-component-communication.md","Vue 3 Engineering Reference — Component Communication",{"type":7,"value":8,"toc":3176},"minimark",[9,14,19,846,850,1163,1167,1655,1659,2105,2109,2284,2288,2664,2668,2790,2794,2955,2959,2963,3041,3172],[10,11,13],"h1",{"id":12},"_06-component-communication","06 — Component Communication",[15,16,18],"h2",{"id":17},"props-down-events-up-the-unidirectional-contract","Props Down, Events Up — The Unidirectional Contract",[20,21,24,291,626,631],"code-wrapper",{"filename":22,"language":23},"Parent.vue","vue",[25,26,30],"pre",{"className":27,"code":28,"language":23,"meta":29,"style":29},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript setup lang=\"ts\">\nimport { ref } from 'vue'\nimport UserForm from '.\u002FUserForm.vue'\n\nconst user = ref({ name: '', email: '' })\n\n\u002F\u002F ── One-way data flow: ────────────────────────────────\n\u002F\u002F Parent → Child: props (read-only in child)\n\u002F\u002F Child → Parent: events (child emits, parent handles)\n\u002F\u002F Child never mutates props directly — emits event, parent updates state.\n\u002F\u002F This ensures a single source of truth and traceable data flow.\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003C!-- Pass data down as props -->\n  \u003CUserForm\n    :model-value=\"user\"\n    @update:model-value=\"user = $event\"\n    @submit=\"handleSubmit\"\n  \u002F>\n  \u003C!-- v-model is sugar for the above two-way binding pattern -->\n\u003C\u002Ftemplate>\n","",[31,32,33,63,79,92,99,129,134,141,147,153,159,165,175,180,190,196,205,225,253,270,276,282],"code",{"__ignoreMap":29},[34,35,38,42,46,50,53,56,60],"span",{"class":36,"line":37},"line",1,[34,39,41],{"class":40},"ssxIu","\u003C",[34,43,45],{"class":44},"sk71V","script",[34,47,49],{"class":48},"sIsaT"," setup",[34,51,52],{"class":48}," lang",[34,54,55],{"class":40},"=",[34,57,59],{"class":58},"sJ6F3","\"ts\"",[34,61,62],{"class":40},">\n",[34,64,66,70,73,76],{"class":36,"line":65},2,[34,67,69],{"class":68},"svdQ7","import",[34,71,72],{"class":40}," { ref } ",[34,74,75],{"class":68},"from",[34,77,78],{"class":58}," 'vue'\n",[34,80,82,84,87,89],{"class":36,"line":81},3,[34,83,69],{"class":68},[34,85,86],{"class":40}," UserForm ",[34,88,75],{"class":68},[34,90,91],{"class":58}," '.\u002FUserForm.vue'\n",[34,93,95],{"class":36,"line":94},4,[34,96,98],{"emptyLinePlaceholder":97},true,"\n",[34,100,102,105,109,112,115,118,121,124,126],{"class":36,"line":101},5,[34,103,104],{"class":68},"const",[34,106,108],{"class":107},"snvgF"," user",[34,110,111],{"class":68}," =",[34,113,114],{"class":48}," ref",[34,116,117],{"class":40},"({ name: ",[34,119,120],{"class":58},"''",[34,122,123],{"class":40},", email: ",[34,125,120],{"class":58},[34,127,128],{"class":40}," })\n",[34,130,132],{"class":36,"line":131},6,[34,133,98],{"emptyLinePlaceholder":97},[34,135,137],{"class":36,"line":136},7,[34,138,140],{"class":139},"sdCPZ","\u002F\u002F ── One-way data flow: ────────────────────────────────\n",[34,142,144],{"class":36,"line":143},8,[34,145,146],{"class":139},"\u002F\u002F Parent → Child: props (read-only in child)\n",[34,148,150],{"class":36,"line":149},9,[34,151,152],{"class":139},"\u002F\u002F Child → Parent: events (child emits, parent handles)\n",[34,154,156],{"class":36,"line":155},10,[34,157,158],{"class":139},"\u002F\u002F Child never mutates props directly — emits event, parent updates state.\n",[34,160,162],{"class":36,"line":161},11,[34,163,164],{"class":139},"\u002F\u002F This ensures a single source of truth and traceable data flow.\n",[34,166,168,171,173],{"class":36,"line":167},12,[34,169,170],{"class":40},"\u003C\u002F",[34,172,45],{"class":44},[34,174,62],{"class":40},[34,176,178],{"class":36,"line":177},13,[34,179,98],{"emptyLinePlaceholder":97},[34,181,183,185,188],{"class":36,"line":182},14,[34,184,41],{"class":40},[34,186,187],{"class":44},"template",[34,189,62],{"class":40},[34,191,193],{"class":36,"line":192},15,[34,194,195],{"class":139},"  \u003C!-- Pass data down as props -->\n",[34,197,199,202],{"class":36,"line":198},16,[34,200,201],{"class":40},"  \u003C",[34,203,204],{"class":44},"UserForm\n",[34,206,208,211,214,216,219,222],{"class":36,"line":207},17,[34,209,210],{"class":40},"    :",[34,212,213],{"class":48},"model-value",[34,215,55],{"class":40},[34,217,218],{"class":58},"\"",[34,220,221],{"class":40},"user",[34,223,224],{"class":58},"\"\n",[34,226,228,231,234,237,239,241,243,246,248,251],{"class":36,"line":227},18,[34,229,230],{"class":40},"    @",[34,232,233],{"class":48},"update",[34,235,236],{"class":40},":",[34,238,213],{"class":48},[34,240,55],{"class":40},[34,242,218],{"class":58},[34,244,245],{"class":40},"user ",[34,247,55],{"class":68},[34,249,250],{"class":40}," $event",[34,252,224],{"class":58},[34,254,256,258,261,263,265,268],{"class":36,"line":255},19,[34,257,230],{"class":40},[34,259,260],{"class":48},"submit",[34,262,55],{"class":40},[34,264,218],{"class":58},[34,266,267],{"class":40},"handleSubmit",[34,269,224],{"class":58},[34,271,273],{"class":36,"line":272},20,[34,274,275],{"class":40},"  \u002F>\n",[34,277,279],{"class":36,"line":278},21,[34,280,281],{"class":139},"  \u003C!-- v-model is sugar for the above two-way binding pattern -->\n",[34,283,285,287,289],{"class":36,"line":284},22,[34,286,170],{"class":40},[34,288,187],{"class":44},[34,290,62],{"class":40},[20,292,294],{"filename":293,"language":23},"UserForm.vue",[25,295,297],{"className":27,"code":296,"language":23,"meta":29,"style":29},"\u003Cscript setup lang=\"ts\">\n\u002F\u002F ── Props are READ-ONLY — mutating a prop is a dev-mode warning ──\n\u002F\u002F Vue's one-way flow: if parent changes the prop, child re-renders with new value.\n\u002F\u002F If child mutated it, the next parent update would overwrite the mutation.\n\nconst props = defineProps\u003C{\n  modelValue: { name: string; email: string }\n}>()\n\nconst emit = defineEmits\u003C{\n  (e: 'update:modelValue', value: { name: string; email: string }): void\n  (e: 'submit'): void\n}>()\n\n\u002F\u002F ── Correct pattern: emit a new object, don't mutate the prop ──\nfunction updateField(field: 'name' | 'email', value: string) {\n  emit('update:modelValue', { ...props.modelValue, [field]: value })\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cinput\n    :value=\"modelValue.name\"\n    @input=\"updateField('name', $event.target.value)\"\n  \u002F>\n  \u003Cbutton @click=\"emit('submit')\">Submit\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n",[31,298,299,315,320,325,330,334,349,381,386,390,404,449,467,471,475,480,516,535,540,548,552,560,567,578,589,594,617],{"__ignoreMap":29},[34,300,301,303,305,307,309,311,313],{"class":36,"line":37},[34,302,41],{"class":40},[34,304,45],{"class":44},[34,306,49],{"class":48},[34,308,52],{"class":48},[34,310,55],{"class":40},[34,312,59],{"class":58},[34,314,62],{"class":40},[34,316,317],{"class":36,"line":65},[34,318,319],{"class":139},"\u002F\u002F ── Props are READ-ONLY — mutating a prop is a dev-mode warning ──\n",[34,321,322],{"class":36,"line":81},[34,323,324],{"class":139},"\u002F\u002F Vue's one-way flow: if parent changes the prop, child re-renders with new value.\n",[34,326,327],{"class":36,"line":94},[34,328,329],{"class":139},"\u002F\u002F If child mutated it, the next parent update would overwrite the mutation.\n",[34,331,332],{"class":36,"line":101},[34,333,98],{"emptyLinePlaceholder":97},[34,335,336,338,341,343,346],{"class":36,"line":131},[34,337,104],{"class":68},[34,339,340],{"class":107}," props",[34,342,111],{"class":68},[34,344,345],{"class":48}," defineProps",[34,347,348],{"class":40},"\u003C{\n",[34,350,351,355,357,360,363,365,368,371,374,376,378],{"class":36,"line":136},[34,352,354],{"class":353},"sCrzJ","  modelValue",[34,356,236],{"class":68},[34,358,359],{"class":40}," { ",[34,361,362],{"class":353},"name",[34,364,236],{"class":68},[34,366,367],{"class":107}," string",[34,369,370],{"class":40},"; ",[34,372,373],{"class":353},"email",[34,375,236],{"class":68},[34,377,367],{"class":107},[34,379,380],{"class":40}," }\n",[34,382,383],{"class":36,"line":143},[34,384,385],{"class":40},"}>()\n",[34,387,388],{"class":36,"line":149},[34,389,98],{"emptyLinePlaceholder":97},[34,391,392,394,397,399,402],{"class":36,"line":155},[34,393,104],{"class":68},[34,395,396],{"class":107}," emit",[34,398,111],{"class":68},[34,400,401],{"class":48}," defineEmits",[34,403,348],{"class":40},[34,405,406,409,412,414,417,420,423,425,427,429,431,433,435,437,439,441,444,446],{"class":36,"line":161},[34,407,408],{"class":40},"  (",[34,410,411],{"class":353},"e",[34,413,236],{"class":68},[34,415,416],{"class":58}," 'update:modelValue'",[34,418,419],{"class":40},", ",[34,421,422],{"class":353},"value",[34,424,236],{"class":68},[34,426,359],{"class":40},[34,428,362],{"class":353},[34,430,236],{"class":68},[34,432,367],{"class":107},[34,434,370],{"class":40},[34,436,373],{"class":353},[34,438,236],{"class":68},[34,440,367],{"class":107},[34,442,443],{"class":40}," })",[34,445,236],{"class":68},[34,447,448],{"class":107}," void\n",[34,450,451,453,455,457,460,463,465],{"class":36,"line":167},[34,452,408],{"class":40},[34,454,411],{"class":353},[34,456,236],{"class":68},[34,458,459],{"class":58}," 'submit'",[34,461,462],{"class":40},")",[34,464,236],{"class":68},[34,466,448],{"class":107},[34,468,469],{"class":36,"line":177},[34,470,385],{"class":40},[34,472,473],{"class":36,"line":182},[34,474,98],{"emptyLinePlaceholder":97},[34,476,477],{"class":36,"line":192},[34,478,479],{"class":139},"\u002F\u002F ── Correct pattern: emit a new object, don't mutate the prop ──\n",[34,481,482,485,488,491,494,496,499,502,505,507,509,511,513],{"class":36,"line":198},[34,483,484],{"class":68},"function",[34,486,487],{"class":48}," updateField",[34,489,490],{"class":40},"(",[34,492,493],{"class":353},"field",[34,495,236],{"class":68},[34,497,498],{"class":58}," 'name'",[34,500,501],{"class":68}," |",[34,503,504],{"class":58}," 'email'",[34,506,419],{"class":40},[34,508,422],{"class":353},[34,510,236],{"class":68},[34,512,367],{"class":107},[34,514,515],{"class":40},") {\n",[34,517,518,521,523,526,529,532],{"class":36,"line":207},[34,519,520],{"class":48},"  emit",[34,522,490],{"class":40},[34,524,525],{"class":58},"'update:modelValue'",[34,527,528],{"class":40},", { ",[34,530,531],{"class":68},"...",[34,533,534],{"class":40},"props.modelValue, [field]: value })\n",[34,536,537],{"class":36,"line":227},[34,538,539],{"class":40},"}\n",[34,541,542,544,546],{"class":36,"line":255},[34,543,170],{"class":40},[34,545,45],{"class":44},[34,547,62],{"class":40},[34,549,550],{"class":36,"line":272},[34,551,98],{"emptyLinePlaceholder":97},[34,553,554,556,558],{"class":36,"line":278},[34,555,41],{"class":40},[34,557,187],{"class":44},[34,559,62],{"class":40},[34,561,562,564],{"class":36,"line":284},[34,563,201],{"class":40},[34,565,566],{"class":44},"input\n",[34,568,570,573,575],{"class":36,"line":569},23,[34,571,572],{"class":48},"    :value",[34,574,55],{"class":40},[34,576,577],{"class":58},"\"modelValue.name\"\n",[34,579,581,584,586],{"class":36,"line":580},24,[34,582,583],{"class":48},"    @input",[34,585,55],{"class":40},[34,587,588],{"class":58},"\"updateField('name', $event.target.value)\"\n",[34,590,592],{"class":36,"line":591},25,[34,593,275],{"class":40},[34,595,597,599,602,605,607,610,613,615],{"class":36,"line":596},26,[34,598,201],{"class":40},[34,600,601],{"class":44},"button",[34,603,604],{"class":48}," @click",[34,606,55],{"class":40},[34,608,609],{"class":58},"\"emit('submit')\"",[34,611,612],{"class":40},">Submit\u003C\u002F",[34,614,601],{"class":44},[34,616,62],{"class":40},[34,618,620,622,624],{"class":36,"line":619},27,[34,621,170],{"class":40},[34,623,187],{"class":44},[34,625,62],{"class":40},[627,628,630],"h3",{"id":629},"anti-pattern-mutating-props-directly","Anti-Pattern: Mutating Props Directly",[20,632,634,734],{"filename":633,"language":23},"MutateProp.vue",[25,635,637],{"className":27,"code":636,"language":23,"meta":29,"style":29},"\u003Cscript setup>\nconst props = defineProps(['user'])\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003C!-- ❌ WRONG: mutating a prop directly — Vue warns in dev mode -->\n  \u003Cinput v-model=\"props.user.name\" \u002F>\n  \u003C!-- Why it breaks: parent owns `user`. If parent re-renders with a new\n       user object, this component's mutation is lost (overwritten by new prop).\n       Also makes data flow untraceable — who changed `user.name`? -->\n\u003C\u002Ftemplate>\n",[31,638,639,649,668,676,680,688,693,711,716,721,726],{"__ignoreMap":29},[34,640,641,643,645,647],{"class":36,"line":37},[34,642,41],{"class":40},[34,644,45],{"class":44},[34,646,49],{"class":48},[34,648,62],{"class":40},[34,650,651,653,655,657,659,662,665],{"class":36,"line":65},[34,652,104],{"class":68},[34,654,340],{"class":107},[34,656,111],{"class":68},[34,658,345],{"class":48},[34,660,661],{"class":40},"([",[34,663,664],{"class":58},"'user'",[34,666,667],{"class":40},"])\n",[34,669,670,672,674],{"class":36,"line":81},[34,671,170],{"class":40},[34,673,45],{"class":44},[34,675,62],{"class":40},[34,677,678],{"class":36,"line":94},[34,679,98],{"emptyLinePlaceholder":97},[34,681,682,684,686],{"class":36,"line":101},[34,683,41],{"class":40},[34,685,187],{"class":44},[34,687,62],{"class":40},[34,689,690],{"class":36,"line":131},[34,691,692],{"class":139},"  \u003C!-- ❌ WRONG: mutating a prop directly — Vue warns in dev mode -->\n",[34,694,695,697,700,703,705,708],{"class":36,"line":136},[34,696,201],{"class":40},[34,698,699],{"class":44},"input",[34,701,702],{"class":48}," v-model",[34,704,55],{"class":40},[34,706,707],{"class":58},"\"props.user.name\"",[34,709,710],{"class":40}," \u002F>\n",[34,712,713],{"class":36,"line":143},[34,714,715],{"class":139},"  \u003C!-- Why it breaks: parent owns `user`. If parent re-renders with a new\n",[34,717,718],{"class":36,"line":149},[34,719,720],{"class":139},"       user object, this component's mutation is lost (overwritten by new prop).\n",[34,722,723],{"class":36,"line":155},[34,724,725],{"class":139},"       Also makes data flow untraceable — who changed `user.name`? -->\n",[34,727,728,730,732],{"class":36,"line":161},[34,729,170],{"class":40},[34,731,187],{"class":44},[34,733,62],{"class":40},[20,735,737],{"filename":736,"language":23},"FixedProp.vue",[25,738,740],{"className":27,"code":739,"language":23,"meta":29,"style":29},"\u003Cscript setup>\nconst props = defineProps(['user'])\nconst emit = defineEmits(['update:user'])\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003C!-- ✅ CORRECT: emit the update, parent owns the mutation -->\n  \u003Cinput\n    :value=\"user.name\"\n    @input=\"emit('update:user', { ...user, name: $event.target.value })\"\n  \u002F>\n\u003C\u002Ftemplate>\n",[31,741,742,752,768,785,793,797,805,810,816,825,834,838],{"__ignoreMap":29},[34,743,744,746,748,750],{"class":36,"line":37},[34,745,41],{"class":40},[34,747,45],{"class":44},[34,749,49],{"class":48},[34,751,62],{"class":40},[34,753,754,756,758,760,762,764,766],{"class":36,"line":65},[34,755,104],{"class":68},[34,757,340],{"class":107},[34,759,111],{"class":68},[34,761,345],{"class":48},[34,763,661],{"class":40},[34,765,664],{"class":58},[34,767,667],{"class":40},[34,769,770,772,774,776,778,780,783],{"class":36,"line":81},[34,771,104],{"class":68},[34,773,396],{"class":107},[34,775,111],{"class":68},[34,777,401],{"class":48},[34,779,661],{"class":40},[34,781,782],{"class":58},"'update:user'",[34,784,667],{"class":40},[34,786,787,789,791],{"class":36,"line":94},[34,788,170],{"class":40},[34,790,45],{"class":44},[34,792,62],{"class":40},[34,794,795],{"class":36,"line":101},[34,796,98],{"emptyLinePlaceholder":97},[34,798,799,801,803],{"class":36,"line":131},[34,800,41],{"class":40},[34,802,187],{"class":44},[34,804,62],{"class":40},[34,806,807],{"class":36,"line":136},[34,808,809],{"class":139},"  \u003C!-- ✅ CORRECT: emit the update, parent owns the mutation -->\n",[34,811,812,814],{"class":36,"line":143},[34,813,201],{"class":40},[34,815,566],{"class":44},[34,817,818,820,822],{"class":36,"line":149},[34,819,572],{"class":48},[34,821,55],{"class":40},[34,823,824],{"class":58},"\"user.name\"\n",[34,826,827,829,831],{"class":36,"line":155},[34,828,583],{"class":48},[34,830,55],{"class":40},[34,832,833],{"class":58},"\"emit('update:user', { ...user, name: $event.target.value })\"\n",[34,835,836],{"class":36,"line":161},[34,837,275],{"class":40},[34,839,840,842,844],{"class":36,"line":167},[34,841,170],{"class":40},[34,843,187],{"class":44},[34,845,62],{"class":40},[15,847,849],{"id":848},"provideinject-typed-dependency-injection","provide\u002Finject — Typed Dependency Injection",[20,851,854],{"filename":852,"language":853},"provide-inject.ts","typescript",[25,855,858],{"className":856,"code":857,"language":853,"meta":29,"style":29},"language-typescript shiki shiki-themes github-light github-dark","import { provide, inject, ref, type InjectionKey, type Ref } from 'vue'\n\n\u002F\u002F ── InjectionKey: typed symbol that carries the TypeScript type ──\n\u002F\u002F Using a symbol (not a string) prevents naming collisions across libraries.\n\u002F\u002F The type is: Ref\u003CUser | null> — injected values are typed correctly.\nexport const USER_KEY: InjectionKey\u003CRef\u003CUser | null>> = Symbol('user')\n\n\u002F\u002F ── Provider: parent or app-level ──\nexport function provideUser() {\n  const user = ref\u003CUser | null>(null)\n  provide(USER_KEY, user)  \u002F\u002F provide the ref — child gets the ref, not just the value\n  return user\n}\n\n\u002F\u002F ── Consumer: any descendant component ──\nexport function useUser() {\n  const user = inject(USER_KEY)\n  if (!user) throw new Error('useUser() must be used within a provider for USER_KEY')\n  \u002F\u002F ⚠️ inject() returns undefined if no provider found — always guard.\n  return user\n}\n\n\u002F\u002F ── Without InjectionKey: string key, no type safety ──\nprovide('theme', 'dark')\nconst theme = inject('theme', 'light')  \u002F\u002F second arg = default if not provided\n\u002F\u002F String keys work but have no type inference — InjectionKey is preferred.\n",[31,859,860,882,886,891,896,901,947,951,956,969,996,1012,1020,1024,1028,1033,1044,1061,1091,1096,1102,1106,1110,1115,1132,1158],{"__ignoreMap":29},[34,861,862,864,867,870,873,875,878,880],{"class":36,"line":37},[34,863,69],{"class":68},[34,865,866],{"class":40}," { provide, inject, ref, ",[34,868,869],{"class":68},"type",[34,871,872],{"class":40}," InjectionKey, ",[34,874,869],{"class":68},[34,876,877],{"class":40}," Ref } ",[34,879,75],{"class":68},[34,881,78],{"class":58},[34,883,884],{"class":36,"line":65},[34,885,98],{"emptyLinePlaceholder":97},[34,887,888],{"class":36,"line":81},[34,889,890],{"class":139},"\u002F\u002F ── InjectionKey: typed symbol that carries the TypeScript type ──\n",[34,892,893],{"class":36,"line":94},[34,894,895],{"class":139},"\u002F\u002F Using a symbol (not a string) prevents naming collisions across libraries.\n",[34,897,898],{"class":36,"line":101},[34,899,900],{"class":139},"\u002F\u002F The type is: Ref\u003CUser | null> — injected values are typed correctly.\n",[34,902,903,906,909,912,914,917,919,922,924,927,929,932,935,937,940,942,944],{"class":36,"line":131},[34,904,905],{"class":68},"export",[34,907,908],{"class":68}," const",[34,910,911],{"class":107}," USER_KEY",[34,913,236],{"class":68},[34,915,916],{"class":48}," InjectionKey",[34,918,41],{"class":40},[34,920,921],{"class":48},"Ref",[34,923,41],{"class":40},[34,925,926],{"class":48},"User",[34,928,501],{"class":68},[34,930,931],{"class":107}," null",[34,933,934],{"class":40},">> ",[34,936,55],{"class":68},[34,938,939],{"class":48}," Symbol",[34,941,490],{"class":40},[34,943,664],{"class":58},[34,945,946],{"class":40},")\n",[34,948,949],{"class":36,"line":136},[34,950,98],{"emptyLinePlaceholder":97},[34,952,953],{"class":36,"line":143},[34,954,955],{"class":139},"\u002F\u002F ── Provider: parent or app-level ──\n",[34,957,958,960,963,966],{"class":36,"line":149},[34,959,905],{"class":68},[34,961,962],{"class":68}," function",[34,964,965],{"class":48}," provideUser",[34,967,968],{"class":40},"() {\n",[34,970,971,974,976,978,980,982,984,986,988,991,994],{"class":36,"line":155},[34,972,973],{"class":68},"  const",[34,975,108],{"class":107},[34,977,111],{"class":68},[34,979,114],{"class":48},[34,981,41],{"class":40},[34,983,926],{"class":48},[34,985,501],{"class":68},[34,987,931],{"class":107},[34,989,990],{"class":40},">(",[34,992,993],{"class":107},"null",[34,995,946],{"class":40},[34,997,998,1001,1003,1006,1009],{"class":36,"line":161},[34,999,1000],{"class":48},"  provide",[34,1002,490],{"class":40},[34,1004,1005],{"class":107},"USER_KEY",[34,1007,1008],{"class":40},", user)  ",[34,1010,1011],{"class":139},"\u002F\u002F provide the ref — child gets the ref, not just the value\n",[34,1013,1014,1017],{"class":36,"line":167},[34,1015,1016],{"class":68},"  return",[34,1018,1019],{"class":40}," user\n",[34,1021,1022],{"class":36,"line":177},[34,1023,539],{"class":40},[34,1025,1026],{"class":36,"line":182},[34,1027,98],{"emptyLinePlaceholder":97},[34,1029,1030],{"class":36,"line":192},[34,1031,1032],{"class":139},"\u002F\u002F ── Consumer: any descendant component ──\n",[34,1034,1035,1037,1039,1042],{"class":36,"line":198},[34,1036,905],{"class":68},[34,1038,962],{"class":68},[34,1040,1041],{"class":48}," useUser",[34,1043,968],{"class":40},[34,1045,1046,1048,1050,1052,1055,1057,1059],{"class":36,"line":207},[34,1047,973],{"class":68},[34,1049,108],{"class":107},[34,1051,111],{"class":68},[34,1053,1054],{"class":48}," inject",[34,1056,490],{"class":40},[34,1058,1005],{"class":107},[34,1060,946],{"class":40},[34,1062,1063,1066,1069,1072,1075,1078,1081,1084,1086,1089],{"class":36,"line":227},[34,1064,1065],{"class":68},"  if",[34,1067,1068],{"class":40}," (",[34,1070,1071],{"class":68},"!",[34,1073,1074],{"class":40},"user) ",[34,1076,1077],{"class":68},"throw",[34,1079,1080],{"class":68}," new",[34,1082,1083],{"class":48}," Error",[34,1085,490],{"class":40},[34,1087,1088],{"class":58},"'useUser() must be used within a provider for USER_KEY'",[34,1090,946],{"class":40},[34,1092,1093],{"class":36,"line":255},[34,1094,1095],{"class":139},"  \u002F\u002F ⚠️ inject() returns undefined if no provider found — always guard.\n",[34,1097,1098,1100],{"class":36,"line":272},[34,1099,1016],{"class":68},[34,1101,1019],{"class":40},[34,1103,1104],{"class":36,"line":278},[34,1105,539],{"class":40},[34,1107,1108],{"class":36,"line":284},[34,1109,98],{"emptyLinePlaceholder":97},[34,1111,1112],{"class":36,"line":569},[34,1113,1114],{"class":139},"\u002F\u002F ── Without InjectionKey: string key, no type safety ──\n",[34,1116,1117,1120,1122,1125,1127,1130],{"class":36,"line":580},[34,1118,1119],{"class":48},"provide",[34,1121,490],{"class":40},[34,1123,1124],{"class":58},"'theme'",[34,1126,419],{"class":40},[34,1128,1129],{"class":58},"'dark'",[34,1131,946],{"class":40},[34,1133,1134,1136,1139,1141,1143,1145,1147,1149,1152,1155],{"class":36,"line":591},[34,1135,104],{"class":68},[34,1137,1138],{"class":107}," theme",[34,1140,111],{"class":68},[34,1142,1054],{"class":48},[34,1144,490],{"class":40},[34,1146,1124],{"class":58},[34,1148,419],{"class":40},[34,1150,1151],{"class":58},"'light'",[34,1153,1154],{"class":40},")  ",[34,1156,1157],{"class":139},"\u002F\u002F second arg = default if not provided\n",[34,1159,1160],{"class":36,"line":596},[34,1161,1162],{"class":139},"\u002F\u002F String keys work but have no type inference — InjectionKey is preferred.\n",[15,1164,1166],{"id":1165},"production-pattern-composable-with-provideinject","Production Pattern — Composable with provide\u002Finject",[20,1168,1170],{"filename":1169,"language":853},"useTheme.ts",[25,1171,1173],{"className":856,"code":1172,"language":853,"meta":29,"style":29},"import { provide, inject, ref, readonly, computed, type InjectionKey } from 'vue'\n\n\u002F\u002F ── Full composable: provider sets up state, consumer accesses via inject ──\n\u002F\u002F Separates \"create\" (provider) from \"use\" (consumer) for clean DI.\n\ninterface ThemeContext {\n  theme: Readonly\u003CRef\u003Cstring>>\n  isDark: Readonly\u003CRef\u003Cboolean>>\n  setTheme: (t: string) => void\n  toggle: () => void\n}\n\nconst THEME_KEY: InjectionKey\u003CThemeContext> = Symbol('theme')\n\n\u002F\u002F ── Provider: called once in the root component ──\nexport function provideTheme(initial = 'light') {\n  const theme = ref(initial)\n  const isDark = computed(() => theme.value === 'dark')\n\n  const context: ThemeContext = {\n    theme: readonly(theme),  \u002F\u002F expose read-only — consumers can't mutate directly\n    isDark: readonly(isDark),\n    setTheme: (t: string) => { theme.value = t },\n    toggle: () => { theme.value = theme.value === 'dark' ? 'light' : 'dark' },\n  }\n\n  provide(THEME_KEY, context)\n  return context  \u002F\u002F provider also gets the mutable API\n}\n\n\u002F\u002F ── Consumer: called in any descendant ──\nexport function useTheme() {\n  const ctx = inject(THEME_KEY)\n  if (!ctx) throw new Error('useTheme() called outside of provideTheme()')\n  return ctx\n}\n\n\u002F\u002F ── Why readonly on injected state: ──────────────────────\n\u002F\u002F Forces consumers to use the provided mutation functions (setTheme, toggle)\n\u002F\u002F instead of directly writing to the ref. Centralizes mutation logic, makes\n\u002F\u002F state changes traceable and debuggable (all mutations go through one path).\n",[31,1174,1175,1191,1195,1200,1205,1209,1220,1242,1262,1286,1300,1304,1308,1337,1341,1346,1367,1380,1408,1412,1427,1441,1451,1477,1510,1515,1519,1531,1542,1547,1552,1558,1570,1588,1613,1621,1626,1631,1637,1643,1649],{"__ignoreMap":29},[34,1176,1177,1179,1182,1184,1187,1189],{"class":36,"line":37},[34,1178,69],{"class":68},[34,1180,1181],{"class":40}," { provide, inject, ref, readonly, computed, ",[34,1183,869],{"class":68},[34,1185,1186],{"class":40}," InjectionKey } ",[34,1188,75],{"class":68},[34,1190,78],{"class":58},[34,1192,1193],{"class":36,"line":65},[34,1194,98],{"emptyLinePlaceholder":97},[34,1196,1197],{"class":36,"line":81},[34,1198,1199],{"class":139},"\u002F\u002F ── Full composable: provider sets up state, consumer accesses via inject ──\n",[34,1201,1202],{"class":36,"line":94},[34,1203,1204],{"class":139},"\u002F\u002F Separates \"create\" (provider) from \"use\" (consumer) for clean DI.\n",[34,1206,1207],{"class":36,"line":101},[34,1208,98],{"emptyLinePlaceholder":97},[34,1210,1211,1214,1217],{"class":36,"line":131},[34,1212,1213],{"class":68},"interface",[34,1215,1216],{"class":48}," ThemeContext",[34,1218,1219],{"class":40}," {\n",[34,1221,1222,1225,1227,1230,1232,1234,1236,1239],{"class":36,"line":136},[34,1223,1224],{"class":353},"  theme",[34,1226,236],{"class":68},[34,1228,1229],{"class":48}," Readonly",[34,1231,41],{"class":40},[34,1233,921],{"class":48},[34,1235,41],{"class":40},[34,1237,1238],{"class":107},"string",[34,1240,1241],{"class":40},">>\n",[34,1243,1244,1247,1249,1251,1253,1255,1257,1260],{"class":36,"line":143},[34,1245,1246],{"class":353},"  isDark",[34,1248,236],{"class":68},[34,1250,1229],{"class":48},[34,1252,41],{"class":40},[34,1254,921],{"class":48},[34,1256,41],{"class":40},[34,1258,1259],{"class":107},"boolean",[34,1261,1241],{"class":40},[34,1263,1264,1267,1269,1271,1274,1276,1278,1281,1284],{"class":36,"line":149},[34,1265,1266],{"class":48},"  setTheme",[34,1268,236],{"class":68},[34,1270,1068],{"class":40},[34,1272,1273],{"class":353},"t",[34,1275,236],{"class":68},[34,1277,367],{"class":107},[34,1279,1280],{"class":40},") ",[34,1282,1283],{"class":68},"=>",[34,1285,448],{"class":107},[34,1287,1288,1291,1293,1296,1298],{"class":36,"line":155},[34,1289,1290],{"class":48},"  toggle",[34,1292,236],{"class":68},[34,1294,1295],{"class":40}," () ",[34,1297,1283],{"class":68},[34,1299,448],{"class":107},[34,1301,1302],{"class":36,"line":161},[34,1303,539],{"class":40},[34,1305,1306],{"class":36,"line":167},[34,1307,98],{"emptyLinePlaceholder":97},[34,1309,1310,1312,1315,1317,1319,1321,1324,1327,1329,1331,1333,1335],{"class":36,"line":177},[34,1311,104],{"class":68},[34,1313,1314],{"class":107}," THEME_KEY",[34,1316,236],{"class":68},[34,1318,916],{"class":48},[34,1320,41],{"class":40},[34,1322,1323],{"class":48},"ThemeContext",[34,1325,1326],{"class":40},"> ",[34,1328,55],{"class":68},[34,1330,939],{"class":48},[34,1332,490],{"class":40},[34,1334,1124],{"class":58},[34,1336,946],{"class":40},[34,1338,1339],{"class":36,"line":182},[34,1340,98],{"emptyLinePlaceholder":97},[34,1342,1343],{"class":36,"line":192},[34,1344,1345],{"class":139},"\u002F\u002F ── Provider: called once in the root component ──\n",[34,1347,1348,1350,1352,1355,1357,1360,1362,1365],{"class":36,"line":198},[34,1349,905],{"class":68},[34,1351,962],{"class":68},[34,1353,1354],{"class":48}," provideTheme",[34,1356,490],{"class":40},[34,1358,1359],{"class":353},"initial",[34,1361,111],{"class":68},[34,1363,1364],{"class":58}," 'light'",[34,1366,515],{"class":40},[34,1368,1369,1371,1373,1375,1377],{"class":36,"line":207},[34,1370,973],{"class":68},[34,1372,1138],{"class":107},[34,1374,111],{"class":68},[34,1376,114],{"class":48},[34,1378,1379],{"class":40},"(initial)\n",[34,1381,1382,1384,1387,1389,1392,1395,1397,1400,1403,1406],{"class":36,"line":227},[34,1383,973],{"class":68},[34,1385,1386],{"class":107}," isDark",[34,1388,111],{"class":68},[34,1390,1391],{"class":48}," computed",[34,1393,1394],{"class":40},"(() ",[34,1396,1283],{"class":68},[34,1398,1399],{"class":40}," theme.value ",[34,1401,1402],{"class":68},"===",[34,1404,1405],{"class":58}," 'dark'",[34,1407,946],{"class":40},[34,1409,1410],{"class":36,"line":255},[34,1411,98],{"emptyLinePlaceholder":97},[34,1413,1414,1416,1419,1421,1423,1425],{"class":36,"line":272},[34,1415,973],{"class":68},[34,1417,1418],{"class":107}," context",[34,1420,236],{"class":68},[34,1422,1216],{"class":48},[34,1424,111],{"class":68},[34,1426,1219],{"class":40},[34,1428,1429,1432,1435,1438],{"class":36,"line":278},[34,1430,1431],{"class":40},"    theme: ",[34,1433,1434],{"class":48},"readonly",[34,1436,1437],{"class":40},"(theme),  ",[34,1439,1440],{"class":139},"\u002F\u002F expose read-only — consumers can't mutate directly\n",[34,1442,1443,1446,1448],{"class":36,"line":284},[34,1444,1445],{"class":40},"    isDark: ",[34,1447,1434],{"class":48},[34,1449,1450],{"class":40},"(isDark),\n",[34,1452,1453,1456,1459,1461,1463,1465,1467,1469,1472,1474],{"class":36,"line":569},[34,1454,1455],{"class":48},"    setTheme",[34,1457,1458],{"class":40},": (",[34,1460,1273],{"class":353},[34,1462,236],{"class":68},[34,1464,367],{"class":107},[34,1466,1280],{"class":40},[34,1468,1283],{"class":68},[34,1470,1471],{"class":40}," { theme.value ",[34,1473,55],{"class":68},[34,1475,1476],{"class":40}," t },\n",[34,1478,1479,1482,1485,1487,1489,1491,1493,1495,1497,1500,1502,1505,1507],{"class":36,"line":580},[34,1480,1481],{"class":48},"    toggle",[34,1483,1484],{"class":40},": () ",[34,1486,1283],{"class":68},[34,1488,1471],{"class":40},[34,1490,55],{"class":68},[34,1492,1399],{"class":40},[34,1494,1402],{"class":68},[34,1496,1405],{"class":58},[34,1498,1499],{"class":68}," ?",[34,1501,1364],{"class":58},[34,1503,1504],{"class":68}," :",[34,1506,1405],{"class":58},[34,1508,1509],{"class":40}," },\n",[34,1511,1512],{"class":36,"line":591},[34,1513,1514],{"class":40},"  }\n",[34,1516,1517],{"class":36,"line":596},[34,1518,98],{"emptyLinePlaceholder":97},[34,1520,1521,1523,1525,1528],{"class":36,"line":619},[34,1522,1000],{"class":48},[34,1524,490],{"class":40},[34,1526,1527],{"class":107},"THEME_KEY",[34,1529,1530],{"class":40},", context)\n",[34,1532,1534,1536,1539],{"class":36,"line":1533},28,[34,1535,1016],{"class":68},[34,1537,1538],{"class":40}," context  ",[34,1540,1541],{"class":139},"\u002F\u002F provider also gets the mutable API\n",[34,1543,1545],{"class":36,"line":1544},29,[34,1546,539],{"class":40},[34,1548,1550],{"class":36,"line":1549},30,[34,1551,98],{"emptyLinePlaceholder":97},[34,1553,1555],{"class":36,"line":1554},31,[34,1556,1557],{"class":139},"\u002F\u002F ── Consumer: called in any descendant ──\n",[34,1559,1561,1563,1565,1568],{"class":36,"line":1560},32,[34,1562,905],{"class":68},[34,1564,962],{"class":68},[34,1566,1567],{"class":48}," useTheme",[34,1569,968],{"class":40},[34,1571,1573,1575,1578,1580,1582,1584,1586],{"class":36,"line":1572},33,[34,1574,973],{"class":68},[34,1576,1577],{"class":107}," ctx",[34,1579,111],{"class":68},[34,1581,1054],{"class":48},[34,1583,490],{"class":40},[34,1585,1527],{"class":107},[34,1587,946],{"class":40},[34,1589,1591,1593,1595,1597,1600,1602,1604,1606,1608,1611],{"class":36,"line":1590},34,[34,1592,1065],{"class":68},[34,1594,1068],{"class":40},[34,1596,1071],{"class":68},[34,1598,1599],{"class":40},"ctx) ",[34,1601,1077],{"class":68},[34,1603,1080],{"class":68},[34,1605,1083],{"class":48},[34,1607,490],{"class":40},[34,1609,1610],{"class":58},"'useTheme() called outside of provideTheme()'",[34,1612,946],{"class":40},[34,1614,1616,1618],{"class":36,"line":1615},35,[34,1617,1016],{"class":68},[34,1619,1620],{"class":40}," ctx\n",[34,1622,1624],{"class":36,"line":1623},36,[34,1625,539],{"class":40},[34,1627,1629],{"class":36,"line":1628},37,[34,1630,98],{"emptyLinePlaceholder":97},[34,1632,1634],{"class":36,"line":1633},38,[34,1635,1636],{"class":139},"\u002F\u002F ── Why readonly on injected state: ──────────────────────\n",[34,1638,1640],{"class":36,"line":1639},39,[34,1641,1642],{"class":139},"\u002F\u002F Forces consumers to use the provided mutation functions (setTheme, toggle)\n",[34,1644,1646],{"class":36,"line":1645},40,[34,1647,1648],{"class":139},"\u002F\u002F instead of directly writing to the ref. Centralizes mutation logic, makes\n",[34,1650,1652],{"class":36,"line":1651},41,[34,1653,1654],{"class":139},"\u002F\u002F state changes traceable and debuggable (all mutations go through one path).\n",[15,1656,1658],{"id":1657},"v-model-on-components-custom-modifiers","v-model on Components — Custom Modifiers",[20,1660,1662],{"filename":1661,"language":23},"PhoneNumberInput.vue",[25,1663,1665],{"className":27,"code":1664,"language":23,"meta":29,"style":29},"\u003Cscript setup>\nimport { computed } from 'vue'\n\n\u002F\u002F ── v-model with built-in modifiers: modelModifiers ──\n\u002F\u002F Parent: \u003CPhoneInput v-model.trim=\"phone\" \u002F>\n\u002F\u002F → modelModifiers = { trim: true }\nconst props = defineProps({\n  modelValue: String,\n  modelModifiers: { default: () => ({}) },\n})\n\nconst emit = defineEmits(['update:modelValue'])\n\n\u002F\u002F ── Custom modifier: format phone numbers as (XXX) XXX-XXXX ──\nfunction formatPhone(value) {\n  const digits = value.replace(\u002F\\D\u002Fg, '').slice(0, 10)\n  if (digits.length \u003C= 3) return digits\n  if (digits.length \u003C= 6) return `(${digits.slice(0, 3)}) ${digits.slice(3)}`\n  return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`\n}\n\nfunction onInput(e) {\n  let value = e.target.value\n  \u002F\u002F Apply custom modifier if present\n  if (props.modelModifiers.format) {\n    value = formatPhone(value)\n  }\n  emit('update:modelValue', value)\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cinput :value=\"modelValue\" @input=\"onInput\" \u002F>\n\u003C\u002Ftemplate>\n",[31,1666,1667,1677,1688,1692,1697,1702,1707,1720,1725,1740,1745,1749,1765,1769,1774,1787,1837,1861,1918,1976,1980,1984,1997,2010,2015,2022,2034,2038,2049,2053,2061,2065,2073,2097],{"__ignoreMap":29},[34,1668,1669,1671,1673,1675],{"class":36,"line":37},[34,1670,41],{"class":40},[34,1672,45],{"class":44},[34,1674,49],{"class":48},[34,1676,62],{"class":40},[34,1678,1679,1681,1684,1686],{"class":36,"line":65},[34,1680,69],{"class":68},[34,1682,1683],{"class":40}," { computed } ",[34,1685,75],{"class":68},[34,1687,78],{"class":58},[34,1689,1690],{"class":36,"line":81},[34,1691,98],{"emptyLinePlaceholder":97},[34,1693,1694],{"class":36,"line":94},[34,1695,1696],{"class":139},"\u002F\u002F ── v-model with built-in modifiers: modelModifiers ──\n",[34,1698,1699],{"class":36,"line":101},[34,1700,1701],{"class":139},"\u002F\u002F Parent: \u003CPhoneInput v-model.trim=\"phone\" \u002F>\n",[34,1703,1704],{"class":36,"line":131},[34,1705,1706],{"class":139},"\u002F\u002F → modelModifiers = { trim: true }\n",[34,1708,1709,1711,1713,1715,1717],{"class":36,"line":136},[34,1710,104],{"class":68},[34,1712,340],{"class":107},[34,1714,111],{"class":68},[34,1716,345],{"class":48},[34,1718,1719],{"class":40},"({\n",[34,1721,1722],{"class":36,"line":143},[34,1723,1724],{"class":40},"  modelValue: String,\n",[34,1726,1727,1730,1733,1735,1737],{"class":36,"line":149},[34,1728,1729],{"class":40},"  modelModifiers: { ",[34,1731,1732],{"class":48},"default",[34,1734,1484],{"class":40},[34,1736,1283],{"class":68},[34,1738,1739],{"class":40}," ({}) },\n",[34,1741,1742],{"class":36,"line":155},[34,1743,1744],{"class":40},"})\n",[34,1746,1747],{"class":36,"line":161},[34,1748,98],{"emptyLinePlaceholder":97},[34,1750,1751,1753,1755,1757,1759,1761,1763],{"class":36,"line":167},[34,1752,104],{"class":68},[34,1754,396],{"class":107},[34,1756,111],{"class":68},[34,1758,401],{"class":48},[34,1760,661],{"class":40},[34,1762,525],{"class":58},[34,1764,667],{"class":40},[34,1766,1767],{"class":36,"line":177},[34,1768,98],{"emptyLinePlaceholder":97},[34,1770,1771],{"class":36,"line":182},[34,1772,1773],{"class":139},"\u002F\u002F ── Custom modifier: format phone numbers as (XXX) XXX-XXXX ──\n",[34,1775,1776,1778,1781,1783,1785],{"class":36,"line":192},[34,1777,484],{"class":68},[34,1779,1780],{"class":48}," formatPhone",[34,1782,490],{"class":40},[34,1784,422],{"class":353},[34,1786,515],{"class":40},[34,1788,1789,1791,1794,1796,1799,1802,1804,1807,1810,1812,1815,1817,1819,1822,1825,1827,1830,1832,1835],{"class":36,"line":198},[34,1790,973],{"class":68},[34,1792,1793],{"class":107}," digits",[34,1795,111],{"class":68},[34,1797,1798],{"class":40}," value.",[34,1800,1801],{"class":48},"replace",[34,1803,490],{"class":40},[34,1805,1806],{"class":58},"\u002F",[34,1808,1809],{"class":107},"\\D",[34,1811,1806],{"class":58},[34,1813,1814],{"class":68},"g",[34,1816,419],{"class":40},[34,1818,120],{"class":58},[34,1820,1821],{"class":40},").",[34,1823,1824],{"class":48},"slice",[34,1826,490],{"class":40},[34,1828,1829],{"class":107},"0",[34,1831,419],{"class":40},[34,1833,1834],{"class":107},"10",[34,1836,946],{"class":40},[34,1838,1839,1841,1844,1847,1850,1853,1855,1858],{"class":36,"line":207},[34,1840,1065],{"class":68},[34,1842,1843],{"class":40}," (digits.",[34,1845,1846],{"class":107},"length",[34,1848,1849],{"class":68}," \u003C=",[34,1851,1852],{"class":107}," 3",[34,1854,1280],{"class":40},[34,1856,1857],{"class":68},"return",[34,1859,1860],{"class":40}," digits\n",[34,1862,1863,1865,1867,1869,1871,1874,1876,1878,1881,1884,1887,1889,1891,1893,1895,1898,1900,1903,1905,1907,1909,1911,1913,1915],{"class":36,"line":227},[34,1864,1065],{"class":68},[34,1866,1843],{"class":40},[34,1868,1846],{"class":107},[34,1870,1849],{"class":68},[34,1872,1873],{"class":107}," 6",[34,1875,1280],{"class":40},[34,1877,1857],{"class":68},[34,1879,1880],{"class":58}," `(${",[34,1882,1883],{"class":40},"digits",[34,1885,1886],{"class":58},".",[34,1888,1824],{"class":48},[34,1890,490],{"class":58},[34,1892,1829],{"class":107},[34,1894,419],{"class":58},[34,1896,1897],{"class":107},"3",[34,1899,462],{"class":58},[34,1901,1902],{"class":58},"}) ${",[34,1904,1883],{"class":40},[34,1906,1886],{"class":58},[34,1908,1824],{"class":48},[34,1910,490],{"class":58},[34,1912,1897],{"class":107},[34,1914,462],{"class":58},[34,1916,1917],{"class":58},"}`\n",[34,1919,1920,1922,1924,1926,1928,1930,1932,1934,1936,1938,1940,1942,1944,1946,1948,1950,1952,1954,1957,1959,1962,1964,1966,1968,1970,1972,1974],{"class":36,"line":255},[34,1921,1016],{"class":68},[34,1923,1880],{"class":58},[34,1925,1883],{"class":40},[34,1927,1886],{"class":58},[34,1929,1824],{"class":48},[34,1931,490],{"class":58},[34,1933,1829],{"class":107},[34,1935,419],{"class":58},[34,1937,1897],{"class":107},[34,1939,462],{"class":58},[34,1941,1902],{"class":58},[34,1943,1883],{"class":40},[34,1945,1886],{"class":58},[34,1947,1824],{"class":48},[34,1949,490],{"class":58},[34,1951,1897],{"class":107},[34,1953,419],{"class":58},[34,1955,1956],{"class":107},"6",[34,1958,462],{"class":58},[34,1960,1961],{"class":58},"}-${",[34,1963,1883],{"class":40},[34,1965,1886],{"class":58},[34,1967,1824],{"class":48},[34,1969,490],{"class":58},[34,1971,1956],{"class":107},[34,1973,462],{"class":58},[34,1975,1917],{"class":58},[34,1977,1978],{"class":36,"line":272},[34,1979,539],{"class":40},[34,1981,1982],{"class":36,"line":278},[34,1983,98],{"emptyLinePlaceholder":97},[34,1985,1986,1988,1991,1993,1995],{"class":36,"line":284},[34,1987,484],{"class":68},[34,1989,1990],{"class":48}," onInput",[34,1992,490],{"class":40},[34,1994,411],{"class":353},[34,1996,515],{"class":40},[34,1998,1999,2002,2005,2007],{"class":36,"line":569},[34,2000,2001],{"class":68},"  let",[34,2003,2004],{"class":40}," value ",[34,2006,55],{"class":68},[34,2008,2009],{"class":40}," e.target.value\n",[34,2011,2012],{"class":36,"line":580},[34,2013,2014],{"class":139},"  \u002F\u002F Apply custom modifier if present\n",[34,2016,2017,2019],{"class":36,"line":591},[34,2018,1065],{"class":68},[34,2020,2021],{"class":40}," (props.modelModifiers.format) {\n",[34,2023,2024,2027,2029,2031],{"class":36,"line":596},[34,2025,2026],{"class":40},"    value ",[34,2028,55],{"class":68},[34,2030,1780],{"class":48},[34,2032,2033],{"class":40},"(value)\n",[34,2035,2036],{"class":36,"line":619},[34,2037,1514],{"class":40},[34,2039,2040,2042,2044,2046],{"class":36,"line":1533},[34,2041,520],{"class":48},[34,2043,490],{"class":40},[34,2045,525],{"class":58},[34,2047,2048],{"class":40},", value)\n",[34,2050,2051],{"class":36,"line":1544},[34,2052,539],{"class":40},[34,2054,2055,2057,2059],{"class":36,"line":1549},[34,2056,170],{"class":40},[34,2058,45],{"class":44},[34,2060,62],{"class":40},[34,2062,2063],{"class":36,"line":1554},[34,2064,98],{"emptyLinePlaceholder":97},[34,2066,2067,2069,2071],{"class":36,"line":1560},[34,2068,41],{"class":40},[34,2070,187],{"class":44},[34,2072,62],{"class":40},[34,2074,2075,2077,2079,2082,2084,2087,2090,2092,2095],{"class":36,"line":1572},[34,2076,201],{"class":40},[34,2078,699],{"class":44},[34,2080,2081],{"class":48}," :value",[34,2083,55],{"class":40},[34,2085,2086],{"class":58},"\"modelValue\"",[34,2088,2089],{"class":48}," @input",[34,2091,55],{"class":40},[34,2093,2094],{"class":58},"\"onInput\"",[34,2096,710],{"class":40},[34,2098,2099,2101,2103],{"class":36,"line":1590},[34,2100,170],{"class":40},[34,2102,187],{"class":44},[34,2104,62],{"class":40},[15,2106,2108],{"id":2107},"multiple-v-model-with-named-bindings","Multiple v-model with Named Bindings",[20,2110,2112],{"filename":2111,"language":23},"DateRangePicker.vue",[25,2113,2115],{"className":27,"code":2114,"language":23,"meta":29,"style":29},"\u003Cscript setup>\n\u002F\u002F ── Named v-models: v-model:startDate and v-model:endDate ──\n\u002F\u002F Parent: \u003CDateRange v-model:start=\"start\" v-model:end=\"end\" \u002F>\n\u002F\u002F Each named v-model gets its own prop + modifier prop:\n\u002F\u002F   start, startModifiers, end, endModifiers\n\ndefineProps({\n  start: String,\n  end: String,\n})\n\nconst emit = defineEmits(['update:start', 'update:end'])\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cinput type=\"date\" :value=\"start\" @input=\"emit('update:start', $event.target.value)\" \u002F>\n  \u003Cinput type=\"date\" :value=\"end\" @input=\"emit('update:end', $event.target.value)\" \u002F>\n\u003C\u002Ftemplate>\n",[31,2116,2117,2127,2132,2137,2142,2147,2151,2158,2163,2168,2172,2176,2198,2206,2210,2218,2248,2276],{"__ignoreMap":29},[34,2118,2119,2121,2123,2125],{"class":36,"line":37},[34,2120,41],{"class":40},[34,2122,45],{"class":44},[34,2124,49],{"class":48},[34,2126,62],{"class":40},[34,2128,2129],{"class":36,"line":65},[34,2130,2131],{"class":139},"\u002F\u002F ── Named v-models: v-model:startDate and v-model:endDate ──\n",[34,2133,2134],{"class":36,"line":81},[34,2135,2136],{"class":139},"\u002F\u002F Parent: \u003CDateRange v-model:start=\"start\" v-model:end=\"end\" \u002F>\n",[34,2138,2139],{"class":36,"line":94},[34,2140,2141],{"class":139},"\u002F\u002F Each named v-model gets its own prop + modifier prop:\n",[34,2143,2144],{"class":36,"line":101},[34,2145,2146],{"class":139},"\u002F\u002F   start, startModifiers, end, endModifiers\n",[34,2148,2149],{"class":36,"line":131},[34,2150,98],{"emptyLinePlaceholder":97},[34,2152,2153,2156],{"class":36,"line":136},[34,2154,2155],{"class":48},"defineProps",[34,2157,1719],{"class":40},[34,2159,2160],{"class":36,"line":143},[34,2161,2162],{"class":40},"  start: String,\n",[34,2164,2165],{"class":36,"line":149},[34,2166,2167],{"class":40},"  end: String,\n",[34,2169,2170],{"class":36,"line":155},[34,2171,1744],{"class":40},[34,2173,2174],{"class":36,"line":161},[34,2175,98],{"emptyLinePlaceholder":97},[34,2177,2178,2180,2182,2184,2186,2188,2191,2193,2196],{"class":36,"line":167},[34,2179,104],{"class":68},[34,2181,396],{"class":107},[34,2183,111],{"class":68},[34,2185,401],{"class":48},[34,2187,661],{"class":40},[34,2189,2190],{"class":58},"'update:start'",[34,2192,419],{"class":40},[34,2194,2195],{"class":58},"'update:end'",[34,2197,667],{"class":40},[34,2199,2200,2202,2204],{"class":36,"line":177},[34,2201,170],{"class":40},[34,2203,45],{"class":44},[34,2205,62],{"class":40},[34,2207,2208],{"class":36,"line":182},[34,2209,98],{"emptyLinePlaceholder":97},[34,2211,2212,2214,2216],{"class":36,"line":192},[34,2213,41],{"class":40},[34,2215,187],{"class":44},[34,2217,62],{"class":40},[34,2219,2220,2222,2224,2227,2229,2232,2234,2236,2239,2241,2243,2246],{"class":36,"line":198},[34,2221,201],{"class":40},[34,2223,699],{"class":44},[34,2225,2226],{"class":48}," type",[34,2228,55],{"class":40},[34,2230,2231],{"class":58},"\"date\"",[34,2233,2081],{"class":48},[34,2235,55],{"class":40},[34,2237,2238],{"class":58},"\"start\"",[34,2240,2089],{"class":48},[34,2242,55],{"class":40},[34,2244,2245],{"class":58},"\"emit('update:start', $event.target.value)\"",[34,2247,710],{"class":40},[34,2249,2250,2252,2254,2256,2258,2260,2262,2264,2267,2269,2271,2274],{"class":36,"line":207},[34,2251,201],{"class":40},[34,2253,699],{"class":44},[34,2255,2226],{"class":48},[34,2257,55],{"class":40},[34,2259,2231],{"class":58},[34,2261,2081],{"class":48},[34,2263,55],{"class":40},[34,2265,2266],{"class":58},"\"end\"",[34,2268,2089],{"class":48},[34,2270,55],{"class":40},[34,2272,2273],{"class":58},"\"emit('update:end', $event.target.value)\"",[34,2275,710],{"class":40},[34,2277,2278,2280,2282],{"class":36,"line":227},[34,2279,170],{"class":40},[34,2281,187],{"class":44},[34,2283,62],{"class":40},[15,2285,2287],{"id":2286},"event-payload-validation-and-v-model-on-custom-components","Event Payload Validation and v-model on Custom Components",[20,2289,2291],{"filename":2290,"language":23},"ValidatedInput.vue",[25,2292,2294],{"className":27,"code":2293,"language":23,"meta":29,"style":29},"\u003Cscript setup>\nimport { ref, computed } from 'vue'\n\nconst props = defineProps({\n  modelValue: String,\n  rules: { type: Array, default: () => [] },  \u002F\u002F validation rules array\n})\n\nconst emit = defineEmits(['update:modelValue', 'validate'])\n\nconst error = ref('')\n\n\u002F\u002F ── Computed validation — re-runs when modelValue or rules change ──\nconst isValid = computed(() => {\n  for (const rule of props.rules) {\n    const result = rule(props.modelValue)\n    if (typeof result === 'string') {\n      error.value = result\n      return false\n    }\n  }\n  error.value = ''\n  return true\n})\n\nfunction onInput(e) {\n  const value = e.target.value\n  emit('update:modelValue', value)\n  \u002F\u002F Emit validation result after the value update — parent can react\n  emit('validate', isValid.value)\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cinput :value=\"modelValue\" @input=\"onInput\" \u002F>\n  \u003Cspan v-if=\"error\" class=\"error\">{{ error }}\u003C\u002Fspan>\n\u003C\u002Ftemplate>\n",[31,2295,2296,2306,2317,2321,2333,2337,2354,2358,2362,2383,2387,2404,2408,2413,2430,2448,2463,2483,2493,2501,2506,2510,2520,2527,2531,2535,2547,2558,2568,2573,2584,2588,2596,2600,2608,2628,2656],{"__ignoreMap":29},[34,2297,2298,2300,2302,2304],{"class":36,"line":37},[34,2299,41],{"class":40},[34,2301,45],{"class":44},[34,2303,49],{"class":48},[34,2305,62],{"class":40},[34,2307,2308,2310,2313,2315],{"class":36,"line":65},[34,2309,69],{"class":68},[34,2311,2312],{"class":40}," { ref, computed } ",[34,2314,75],{"class":68},[34,2316,78],{"class":58},[34,2318,2319],{"class":36,"line":81},[34,2320,98],{"emptyLinePlaceholder":97},[34,2322,2323,2325,2327,2329,2331],{"class":36,"line":94},[34,2324,104],{"class":68},[34,2326,340],{"class":107},[34,2328,111],{"class":68},[34,2330,345],{"class":48},[34,2332,1719],{"class":40},[34,2334,2335],{"class":36,"line":101},[34,2336,1724],{"class":40},[34,2338,2339,2342,2344,2346,2348,2351],{"class":36,"line":131},[34,2340,2341],{"class":40},"  rules: { type: Array, ",[34,2343,1732],{"class":48},[34,2345,1484],{"class":40},[34,2347,1283],{"class":68},[34,2349,2350],{"class":40}," [] },  ",[34,2352,2353],{"class":139},"\u002F\u002F validation rules array\n",[34,2355,2356],{"class":36,"line":136},[34,2357,1744],{"class":40},[34,2359,2360],{"class":36,"line":143},[34,2361,98],{"emptyLinePlaceholder":97},[34,2363,2364,2366,2368,2370,2372,2374,2376,2378,2381],{"class":36,"line":149},[34,2365,104],{"class":68},[34,2367,396],{"class":107},[34,2369,111],{"class":68},[34,2371,401],{"class":48},[34,2373,661],{"class":40},[34,2375,525],{"class":58},[34,2377,419],{"class":40},[34,2379,2380],{"class":58},"'validate'",[34,2382,667],{"class":40},[34,2384,2385],{"class":36,"line":155},[34,2386,98],{"emptyLinePlaceholder":97},[34,2388,2389,2391,2394,2396,2398,2400,2402],{"class":36,"line":161},[34,2390,104],{"class":68},[34,2392,2393],{"class":107}," error",[34,2395,111],{"class":68},[34,2397,114],{"class":48},[34,2399,490],{"class":40},[34,2401,120],{"class":58},[34,2403,946],{"class":40},[34,2405,2406],{"class":36,"line":167},[34,2407,98],{"emptyLinePlaceholder":97},[34,2409,2410],{"class":36,"line":177},[34,2411,2412],{"class":139},"\u002F\u002F ── Computed validation — re-runs when modelValue or rules change ──\n",[34,2414,2415,2417,2420,2422,2424,2426,2428],{"class":36,"line":182},[34,2416,104],{"class":68},[34,2418,2419],{"class":107}," isValid",[34,2421,111],{"class":68},[34,2423,1391],{"class":48},[34,2425,1394],{"class":40},[34,2427,1283],{"class":68},[34,2429,1219],{"class":40},[34,2431,2432,2435,2437,2439,2442,2445],{"class":36,"line":192},[34,2433,2434],{"class":68},"  for",[34,2436,1068],{"class":40},[34,2438,104],{"class":68},[34,2440,2441],{"class":107}," rule",[34,2443,2444],{"class":68}," of",[34,2446,2447],{"class":40}," props.rules) {\n",[34,2449,2450,2453,2456,2458,2460],{"class":36,"line":198},[34,2451,2452],{"class":68},"    const",[34,2454,2455],{"class":107}," result",[34,2457,111],{"class":68},[34,2459,2441],{"class":48},[34,2461,2462],{"class":40},"(props.modelValue)\n",[34,2464,2465,2468,2470,2473,2476,2478,2481],{"class":36,"line":207},[34,2466,2467],{"class":68},"    if",[34,2469,1068],{"class":40},[34,2471,2472],{"class":68},"typeof",[34,2474,2475],{"class":40}," result ",[34,2477,1402],{"class":68},[34,2479,2480],{"class":58}," 'string'",[34,2482,515],{"class":40},[34,2484,2485,2488,2490],{"class":36,"line":227},[34,2486,2487],{"class":40},"      error.value ",[34,2489,55],{"class":68},[34,2491,2492],{"class":40}," result\n",[34,2494,2495,2498],{"class":36,"line":255},[34,2496,2497],{"class":68},"      return",[34,2499,2500],{"class":107}," false\n",[34,2502,2503],{"class":36,"line":272},[34,2504,2505],{"class":40},"    }\n",[34,2507,2508],{"class":36,"line":278},[34,2509,1514],{"class":40},[34,2511,2512,2515,2517],{"class":36,"line":284},[34,2513,2514],{"class":40},"  error.value ",[34,2516,55],{"class":68},[34,2518,2519],{"class":58}," ''\n",[34,2521,2522,2524],{"class":36,"line":569},[34,2523,1016],{"class":68},[34,2525,2526],{"class":107}," true\n",[34,2528,2529],{"class":36,"line":580},[34,2530,1744],{"class":40},[34,2532,2533],{"class":36,"line":591},[34,2534,98],{"emptyLinePlaceholder":97},[34,2536,2537,2539,2541,2543,2545],{"class":36,"line":596},[34,2538,484],{"class":68},[34,2540,1990],{"class":48},[34,2542,490],{"class":40},[34,2544,411],{"class":353},[34,2546,515],{"class":40},[34,2548,2549,2551,2554,2556],{"class":36,"line":619},[34,2550,973],{"class":68},[34,2552,2553],{"class":107}," value",[34,2555,111],{"class":68},[34,2557,2009],{"class":40},[34,2559,2560,2562,2564,2566],{"class":36,"line":1533},[34,2561,520],{"class":48},[34,2563,490],{"class":40},[34,2565,525],{"class":58},[34,2567,2048],{"class":40},[34,2569,2570],{"class":36,"line":1544},[34,2571,2572],{"class":139},"  \u002F\u002F Emit validation result after the value update — parent can react\n",[34,2574,2575,2577,2579,2581],{"class":36,"line":1549},[34,2576,520],{"class":48},[34,2578,490],{"class":40},[34,2580,2380],{"class":58},[34,2582,2583],{"class":40},", isValid.value)\n",[34,2585,2586],{"class":36,"line":1554},[34,2587,539],{"class":40},[34,2589,2590,2592,2594],{"class":36,"line":1560},[34,2591,170],{"class":40},[34,2593,45],{"class":44},[34,2595,62],{"class":40},[34,2597,2598],{"class":36,"line":1572},[34,2599,98],{"emptyLinePlaceholder":97},[34,2601,2602,2604,2606],{"class":36,"line":1590},[34,2603,41],{"class":40},[34,2605,187],{"class":44},[34,2607,62],{"class":40},[34,2609,2610,2612,2614,2616,2618,2620,2622,2624,2626],{"class":36,"line":1615},[34,2611,201],{"class":40},[34,2613,699],{"class":44},[34,2615,2081],{"class":48},[34,2617,55],{"class":40},[34,2619,2086],{"class":58},[34,2621,2089],{"class":48},[34,2623,55],{"class":40},[34,2625,2094],{"class":58},[34,2627,710],{"class":40},[34,2629,2630,2632,2634,2637,2639,2642,2645,2647,2649,2652,2654],{"class":36,"line":1623},[34,2631,201],{"class":40},[34,2633,34],{"class":44},[34,2635,2636],{"class":48}," v-if",[34,2638,55],{"class":40},[34,2640,2641],{"class":58},"\"error\"",[34,2643,2644],{"class":48}," class",[34,2646,55],{"class":40},[34,2648,2641],{"class":58},[34,2650,2651],{"class":40},">{{ error }}\u003C\u002F",[34,2653,34],{"class":44},[34,2655,62],{"class":40},[34,2657,2658,2660,2662],{"class":36,"line":1628},[34,2659,170],{"class":40},[34,2661,187],{"class":44},[34,2663,62],{"class":40},[15,2665,2667],{"id":2666},"tips-tricks","💡 Tips & Tricks",[20,2669,2671],{"filename":2670,"language":853},"tips.ts",[25,2672,2674],{"className":856,"code":2673,"language":853,"meta":29,"style":29},"\u002F\u002F ── 1. provide\u002Finject with factory default ──\nconst config = inject(CONFIG_KEY, () => createDefaultConfig(), true)\n\u002F\u002F 3rd arg true: treat the 2nd arg as a factory function (like prop defaults)\n\n\u002F\u002F ── 2. Reactive provide — pass the ref, not the value ──\n\u002F\u002F ✅ provide(KEY, myRef) — child injects the ref, stays reactive\n\u002F\u002F ❌ provide(KEY, myRef.value) — child gets a static value, no reactivity\n\n\u002F\u002F ── 3. Event naming: kebab-case in template, camelCase in emit ──\n\u002F\u002F emit('update:modelValue') → parent listens: @update:model-value\n\u002F\u002F Vue auto-converts camelCase emits to kebab-case in templates.\n\n\u002F\u002F ── 4. useAttrs() — access fallthrough attrs in script ──\nconst attrs = useAttrs()\n\u002F\u002F Equivalent to $attrs in template, but accessible in \u003Cscript setup>.\n\u002F\u002F Useful for forwarding attrs to specific children in wrapper components.\n",[31,2675,2676,2681,2713,2718,2722,2727,2732,2737,2741,2746,2751,2756,2760,2765,2780,2785],{"__ignoreMap":29},[34,2677,2678],{"class":36,"line":37},[34,2679,2680],{"class":139},"\u002F\u002F ── 1. provide\u002Finject with factory default ──\n",[34,2682,2683,2685,2688,2690,2692,2694,2697,2700,2702,2705,2708,2711],{"class":36,"line":65},[34,2684,104],{"class":68},[34,2686,2687],{"class":107}," config",[34,2689,111],{"class":68},[34,2691,1054],{"class":48},[34,2693,490],{"class":40},[34,2695,2696],{"class":107},"CONFIG_KEY",[34,2698,2699],{"class":40},", () ",[34,2701,1283],{"class":68},[34,2703,2704],{"class":48}," createDefaultConfig",[34,2706,2707],{"class":40},"(), ",[34,2709,2710],{"class":107},"true",[34,2712,946],{"class":40},[34,2714,2715],{"class":36,"line":81},[34,2716,2717],{"class":139},"\u002F\u002F 3rd arg true: treat the 2nd arg as a factory function (like prop defaults)\n",[34,2719,2720],{"class":36,"line":94},[34,2721,98],{"emptyLinePlaceholder":97},[34,2723,2724],{"class":36,"line":101},[34,2725,2726],{"class":139},"\u002F\u002F ── 2. Reactive provide — pass the ref, not the value ──\n",[34,2728,2729],{"class":36,"line":131},[34,2730,2731],{"class":139},"\u002F\u002F ✅ provide(KEY, myRef) — child injects the ref, stays reactive\n",[34,2733,2734],{"class":36,"line":136},[34,2735,2736],{"class":139},"\u002F\u002F ❌ provide(KEY, myRef.value) — child gets a static value, no reactivity\n",[34,2738,2739],{"class":36,"line":143},[34,2740,98],{"emptyLinePlaceholder":97},[34,2742,2743],{"class":36,"line":149},[34,2744,2745],{"class":139},"\u002F\u002F ── 3. Event naming: kebab-case in template, camelCase in emit ──\n",[34,2747,2748],{"class":36,"line":155},[34,2749,2750],{"class":139},"\u002F\u002F emit('update:modelValue') → parent listens: @update:model-value\n",[34,2752,2753],{"class":36,"line":161},[34,2754,2755],{"class":139},"\u002F\u002F Vue auto-converts camelCase emits to kebab-case in templates.\n",[34,2757,2758],{"class":36,"line":167},[34,2759,98],{"emptyLinePlaceholder":97},[34,2761,2762],{"class":36,"line":177},[34,2763,2764],{"class":139},"\u002F\u002F ── 4. useAttrs() — access fallthrough attrs in script ──\n",[34,2766,2767,2769,2772,2774,2777],{"class":36,"line":182},[34,2768,104],{"class":68},[34,2770,2771],{"class":107}," attrs",[34,2773,111],{"class":68},[34,2775,2776],{"class":48}," useAttrs",[34,2778,2779],{"class":40},"()\n",[34,2781,2782],{"class":36,"line":192},[34,2783,2784],{"class":139},"\u002F\u002F Equivalent to $attrs in template, but accessible in \u003Cscript setup>.\n",[34,2786,2787],{"class":36,"line":198},[34,2788,2789],{"class":139},"\u002F\u002F Useful for forwarding attrs to specific children in wrapper components.\n",[15,2791,2793],{"id":2792},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[20,2795,2797],{"filename":2796,"language":853},"edge-cases.ts",[25,2798,2800],{"className":856,"code":2799,"language":853,"meta":29,"style":29},"\u002F\u002F ── 1. Injecting a non-provided key returns undefined, not an error ──\nconst user = inject(USER_KEY)  \u002F\u002F undefined if no provider — always guard\nif (!user) throw new Error('USER_KEY not provided')\n\u002F\u002F Or use a default: inject(USER_KEY, ref(null))\n\n\u002F\u002F ── 2. provide\u002Finject is NOT reactive across component boundaries ──\n\u002F\u002F If you provide a plain value (not a ref), changes to it in the provider\n\u002F\u002F do NOT update consumers. Always provide refs or reactive objects.\n\n\u002F\u002F ── 3. Prop mutation in child — silent failure in production ──\n\u002F\u002F Dev mode: Vue warns \"Avoid mutating prop directly.\"\n\u002F\u002F Prod mode: mutation works... until parent re-renders and overwrites it.\n\u002F\u002F This causes \"works in dev, breaks in prod\" bugs.\n\n\u002F\u002F ── 4. Event names must match exactly (case-sensitive in emit) ──\n\u002F\u002F emit('update:modelValue') + parent @update:model-value → ✅ (auto kebab)\n\u002F\u002F emit('updateModelValue') + parent @update-model-value → ❌ (no match)\n\n\u002F\u002F ── 5. provide\u002Finject is hierarchical — not broadcast ──\n\u002F\u002F Only DESCENDANT components can inject. Siblings cannot.\n\u002F\u002F For cross-tree communication, use a Pinia store or event bus pattern.\n\n\u002F\u002F ── 6. v-model modifiers prop is always an object ──\n\u002F\u002F modelModifiers default is () => ({}) — never undefined.\n\u002F\u002F Check with: if (props.modelModifiers.trim) { ... }\n",[31,2801,2802,2807,2826,2850,2855,2859,2864,2869,2874,2878,2883,2888,2893,2898,2902,2907,2912,2917,2921,2926,2931,2936,2940,2945,2950],{"__ignoreMap":29},[34,2803,2804],{"class":36,"line":37},[34,2805,2806],{"class":139},"\u002F\u002F ── 1. Injecting a non-provided key returns undefined, not an error ──\n",[34,2808,2809,2811,2813,2815,2817,2819,2821,2823],{"class":36,"line":65},[34,2810,104],{"class":68},[34,2812,108],{"class":107},[34,2814,111],{"class":68},[34,2816,1054],{"class":48},[34,2818,490],{"class":40},[34,2820,1005],{"class":107},[34,2822,1154],{"class":40},[34,2824,2825],{"class":139},"\u002F\u002F undefined if no provider — always guard\n",[34,2827,2828,2831,2833,2835,2837,2839,2841,2843,2845,2848],{"class":36,"line":81},[34,2829,2830],{"class":68},"if",[34,2832,1068],{"class":40},[34,2834,1071],{"class":68},[34,2836,1074],{"class":40},[34,2838,1077],{"class":68},[34,2840,1080],{"class":68},[34,2842,1083],{"class":48},[34,2844,490],{"class":40},[34,2846,2847],{"class":58},"'USER_KEY not provided'",[34,2849,946],{"class":40},[34,2851,2852],{"class":36,"line":94},[34,2853,2854],{"class":139},"\u002F\u002F Or use a default: inject(USER_KEY, ref(null))\n",[34,2856,2857],{"class":36,"line":101},[34,2858,98],{"emptyLinePlaceholder":97},[34,2860,2861],{"class":36,"line":131},[34,2862,2863],{"class":139},"\u002F\u002F ── 2. provide\u002Finject is NOT reactive across component boundaries ──\n",[34,2865,2866],{"class":36,"line":136},[34,2867,2868],{"class":139},"\u002F\u002F If you provide a plain value (not a ref), changes to it in the provider\n",[34,2870,2871],{"class":36,"line":143},[34,2872,2873],{"class":139},"\u002F\u002F do NOT update consumers. Always provide refs or reactive objects.\n",[34,2875,2876],{"class":36,"line":149},[34,2877,98],{"emptyLinePlaceholder":97},[34,2879,2880],{"class":36,"line":155},[34,2881,2882],{"class":139},"\u002F\u002F ── 3. Prop mutation in child — silent failure in production ──\n",[34,2884,2885],{"class":36,"line":161},[34,2886,2887],{"class":139},"\u002F\u002F Dev mode: Vue warns \"Avoid mutating prop directly.\"\n",[34,2889,2890],{"class":36,"line":167},[34,2891,2892],{"class":139},"\u002F\u002F Prod mode: mutation works... until parent re-renders and overwrites it.\n",[34,2894,2895],{"class":36,"line":177},[34,2896,2897],{"class":139},"\u002F\u002F This causes \"works in dev, breaks in prod\" bugs.\n",[34,2899,2900],{"class":36,"line":182},[34,2901,98],{"emptyLinePlaceholder":97},[34,2903,2904],{"class":36,"line":192},[34,2905,2906],{"class":139},"\u002F\u002F ── 4. Event names must match exactly (case-sensitive in emit) ──\n",[34,2908,2909],{"class":36,"line":198},[34,2910,2911],{"class":139},"\u002F\u002F emit('update:modelValue') + parent @update:model-value → ✅ (auto kebab)\n",[34,2913,2914],{"class":36,"line":207},[34,2915,2916],{"class":139},"\u002F\u002F emit('updateModelValue') + parent @update-model-value → ❌ (no match)\n",[34,2918,2919],{"class":36,"line":227},[34,2920,98],{"emptyLinePlaceholder":97},[34,2922,2923],{"class":36,"line":255},[34,2924,2925],{"class":139},"\u002F\u002F ── 5. provide\u002Finject is hierarchical — not broadcast ──\n",[34,2927,2928],{"class":36,"line":272},[34,2929,2930],{"class":139},"\u002F\u002F Only DESCENDANT components can inject. Siblings cannot.\n",[34,2932,2933],{"class":36,"line":278},[34,2934,2935],{"class":139},"\u002F\u002F For cross-tree communication, use a Pinia store or event bus pattern.\n",[34,2937,2938],{"class":36,"line":284},[34,2939,98],{"emptyLinePlaceholder":97},[34,2941,2942],{"class":36,"line":569},[34,2943,2944],{"class":139},"\u002F\u002F ── 6. v-model modifiers prop is always an object ──\n",[34,2946,2947],{"class":36,"line":580},[34,2948,2949],{"class":139},"\u002F\u002F modelModifiers default is () => ({}) — never undefined.\n",[34,2951,2952],{"class":36,"line":591},[34,2953,2954],{"class":139},"\u002F\u002F Check with: if (props.modelModifiers.trim) { ... }\n",[15,2956,2958],{"id":2957},"spot-the-bug","🧠 Spot the Bug",[2960,2961,2962],"p",{},"A child component mutates a prop object's nested property, but the parent's state doesn't update.",[20,2964,2966],{"filename":2965,"language":23},"PropMutationBug.vue",[25,2967,2969],{"className":27,"code":2968,"language":23,"meta":29,"style":29},"\u003Cscript setup>\nconst props = defineProps({ user: Object })\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003C!-- Mutating a nested property of a prop object -->\n  \u003Cinput v-model=\"props.user.name\" \u002F>\n\u003C\u002Ftemplate>\n",[31,2970,2971,2981,2994,3002,3006,3014,3019,3033],{"__ignoreMap":29},[34,2972,2973,2975,2977,2979],{"class":36,"line":37},[34,2974,41],{"class":40},[34,2976,45],{"class":44},[34,2978,49],{"class":48},[34,2980,62],{"class":40},[34,2982,2983,2985,2987,2989,2991],{"class":36,"line":65},[34,2984,104],{"class":68},[34,2986,340],{"class":107},[34,2988,111],{"class":68},[34,2990,345],{"class":48},[34,2992,2993],{"class":40},"({ user: Object })\n",[34,2995,2996,2998,3000],{"class":36,"line":81},[34,2997,170],{"class":40},[34,2999,45],{"class":44},[34,3001,62],{"class":40},[34,3003,3004],{"class":36,"line":94},[34,3005,98],{"emptyLinePlaceholder":97},[34,3007,3008,3010,3012],{"class":36,"line":101},[34,3009,41],{"class":40},[34,3011,187],{"class":44},[34,3013,62],{"class":40},[34,3015,3016],{"class":36,"line":131},[34,3017,3018],{"class":139},"  \u003C!-- Mutating a nested property of a prop object -->\n",[34,3020,3021,3023,3025,3027,3029,3031],{"class":36,"line":136},[34,3022,201],{"class":40},[34,3024,699],{"class":44},[34,3026,702],{"class":48},[34,3028,55],{"class":40},[34,3030,707],{"class":58},[34,3032,710],{"class":40},[34,3034,3035,3037,3039],{"class":36,"line":143},[34,3036,170],{"class":40},[34,3038,187],{"class":44},[34,3040,62],{"class":40},[3042,3043,3044,3048,3059,3066,3166],"details",{},[3045,3046,3047],"summary",{},"Answer",[2960,3049,3050,3051,3055,3056,3058],{},"Mutating a prop object's nested property ",[3052,3053,3054],"em",{},"appears"," to work (the object is passed by reference), but it violates Vue's one-way data flow. The parent doesn't know the object changed, so it won't trigger any watchers or re-renders in the parent. If the parent later replaces the ",[31,3057,221],{}," object (e.g., after a fetch), the child's mutation is silently lost.",[2960,3060,3061,3065],{},[3062,3063,3064],"strong",{},"Fix"," — emit the update, parent owns the mutation:",[20,3067,3069],{"filename":3068,"language":23},"PropMutationFixed.vue",[25,3070,3072],{"className":27,"code":3071,"language":23,"meta":29,"style":29},"\u003Cscript setup>\nconst props = defineProps({ user: Object })\nconst emit = defineEmits(['update:user'])\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cinput\n    :value=\"user.name\"\n    @input=\"emit('update:user', { ...user, name: $event.target.value })\"\n  \u002F>\n\u003C\u002Ftemplate>\n",[31,3073,3074,3084,3096,3112,3120,3124,3132,3138,3146,3154,3158],{"__ignoreMap":29},[34,3075,3076,3078,3080,3082],{"class":36,"line":37},[34,3077,41],{"class":40},[34,3079,45],{"class":44},[34,3081,49],{"class":48},[34,3083,62],{"class":40},[34,3085,3086,3088,3090,3092,3094],{"class":36,"line":65},[34,3087,104],{"class":68},[34,3089,340],{"class":107},[34,3091,111],{"class":68},[34,3093,345],{"class":48},[34,3095,2993],{"class":40},[34,3097,3098,3100,3102,3104,3106,3108,3110],{"class":36,"line":81},[34,3099,104],{"class":68},[34,3101,396],{"class":107},[34,3103,111],{"class":68},[34,3105,401],{"class":48},[34,3107,661],{"class":40},[34,3109,782],{"class":58},[34,3111,667],{"class":40},[34,3113,3114,3116,3118],{"class":36,"line":94},[34,3115,170],{"class":40},[34,3117,45],{"class":44},[34,3119,62],{"class":40},[34,3121,3122],{"class":36,"line":101},[34,3123,98],{"emptyLinePlaceholder":97},[34,3125,3126,3128,3130],{"class":36,"line":131},[34,3127,41],{"class":40},[34,3129,187],{"class":44},[34,3131,62],{"class":40},[34,3133,3134,3136],{"class":36,"line":136},[34,3135,201],{"class":40},[34,3137,566],{"class":44},[34,3139,3140,3142,3144],{"class":36,"line":143},[34,3141,572],{"class":48},[34,3143,55],{"class":40},[34,3145,824],{"class":58},[34,3147,3148,3150,3152],{"class":36,"line":149},[34,3149,583],{"class":48},[34,3151,55],{"class":40},[34,3153,833],{"class":58},[34,3155,3156],{"class":36,"line":155},[34,3157,275],{"class":40},[34,3159,3160,3162,3164],{"class":36,"line":161},[34,3161,170],{"class":40},[34,3163,187],{"class":44},[34,3165,62],{"class":40},[2960,3167,3168,3171],{},[3062,3169,3170],{},"The lesson",": props are read-only contracts. Even nested mutations work at the JS level but break Vue's reactivity tracking. Always emit updates — the parent is the single source of truth.",[3173,3174,3175],"style",{},"html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sk71V, html code.shiki .sk71V{--shiki-default:#22863A;--shiki-github-dark:#85E89D}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}",{"title":29,"searchDepth":65,"depth":65,"links":3177},[3178,3181,3182,3183,3184,3185,3186,3187,3188],{"id":17,"depth":65,"text":18,"children":3179},[3180],{"id":629,"depth":81,"text":630},{"id":848,"depth":65,"text":849},{"id":1165,"depth":65,"text":1166},{"id":1657,"depth":65,"text":1658},{"id":2107,"depth":65,"text":2108},{"id":2286,"depth":65,"text":2287},{"id":2666,"depth":65,"text":2667},{"id":2792,"depth":65,"text":2793},{"id":2957,"depth":65,"text":2958},"Props down\u002Fevents up contract, provide\u002Finject dependency injection with typed symbols, $attrs forwarding, event bus alternatives, and v-model component patterns for production forms.","md",{},"\u002Fvue\u002F06-component-communication",{"title":5,"description":3189},"vue\u002F06-component-communication","Gux6gP_zdof2zXoT8Alw_PsgvnHAshbLetoGZeoaafA",1789924655482]