{"record":{"id":"e0694c00c5b298b0","repo":"vuetifyjs/vuetify","slug":"vuetify-v-expansion-panel-text-needs-to-be-place","errorCode":null,"errorMessage":"[Vuetify] v-expansion-panel-text needs to be placed inside v-expansion-panel","messagePattern":"\\[Vuetify\\] v-expansion-panel-text needs to be placed inside v-expansion-panel","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vuetify/src/components/VExpansionPanel/VExpansionPanelText.tsx","lineNumber":26,"sourceCode":"\n// Utilities\nimport { inject } from 'vue'\nimport { genericComponent, propsFactory, useRender } from '@/util'\n\nexport const makeVExpansionPanelTextProps = propsFactory({\n  ...makeComponentProps(),\n  ...makeLazyProps(),\n}, 'VExpansionPanelText')\n\nexport const VExpansionPanelText = genericComponent()({\n  name: 'VExpansionPanelText',\n\n  props: makeVExpansionPanelTextProps(),\n\n  setup (props, { slots }) {\n    const expansionPanel = inject(VExpansionPanelSymbol)\n\n    if (!expansionPanel) throw new Error('[Vuetify] v-expansion-panel-text needs to be placed inside v-expansion-panel')\n\n    const { hasContent, onAfterLeave } = useLazy(props, expansionPanel.isSelected)\n\n    useRender(() => (\n      <VExpandTransition onAfterLeave={ onAfterLeave }>\n        <div\n          class={[\n            'v-expansion-panel-text',\n            props.class,\n          ]}\n          style={ props.style }\n          v-show={ expansionPanel.isSelected.value }\n        >\n          { slots.default && hasContent.value && (\n            <div class=\"v-expansion-panel-text__wrapper\">\n              { slots.default?.() }\n            </div>\n          )}","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/vuetifyjs/vuetify/blob/8d153908dffb7592eb389ddbfbab955a0ccc977f/packages/vuetify/src/components/VExpansionPanel/VExpansionPanelText.tsx#L8-L44","documentation":"VExpansionPanelText injects VExpansionPanelSymbol, provided only by <v-expansion-panel>. It throws when <v-expansion-panel-text> is rendered without a <v-expansion-panel> ancestor, because there is no panel selection/state to bind to.","triggerScenarios":"Placing <v-expansion-panel-text> outside <v-expansion-panel>; rendering it via a teleport whose target is outside the panel tree; testing it in isolation.","commonSituations":"Copy-paste of panel text into a non-panel container; restructuring markup that drops the panel wrapper; custom builds that assemble panels dynamically and miss the wrapper.","solutions":["Nest <v-expansion-panel-text> inside <v-expansion-panel> (and that inside <v-expansion-panels>).","Keep teleported content within the panel's provide tree.","In tests, wrap the component in <v-expansion-panel>."],"exampleFix":"// before\n<v-expansion-panel-text>Content</v-expansion-panel-text>\n\n// after\n<v-expansion-panels>\n  <v-expansion-panel>\n    <v-expansion-panel-title>Title</v-expansion-panel-title>\n    <v-expansion-panel-text>Content</v-expansion-panel-text>\n  </v-expansion-panel>\n</v-expansion-panels>","handlingStrategy":"type-guard","validationCode":"import { inject } from 'vue'\nimport { VExpansionPanelSymbol } from 'vuetify'\nconst panel = inject(VExpansionPanelSymbol, null)\nif (!panel) {\n  // not inside a panel: render a plain div instead of v-expansion-panel-text\n}","typeGuard":"import { inject } from 'vue'\nimport { VExpansionPanelSymbol } from 'vuetify'\nexport function isInsideExpansionPanel (): boolean {\n  return inject(VExpansionPanelSymbol, null) != null\n}","tryCatchPattern":null,"preventionTips":["Always nest panel text/title inside <v-expansion-panel>.","Do not teleport panel children outside the panel tree.","Wrap children in <v-expansion-panel> for tests."],"tags":["injection","expansion-panel","components"],"backgroundTag":null,"analyzedSha":"8d153908dffb7592eb389ddbfbab955a0ccc977f","analyzedAt":"2026-08-12T21:54:20.596Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}