vuetifyjs/vuetify · error · Error
[Vuetify] VOtpField must be used inside VOtpInput
Error message
[Vuetify] VOtpField must be used inside VOtpInput
What it means
VOtpField injects VOtpInputSymbol, provided only by <v-otp-input>. It throws when <v-otp-field> is rendered outside <v-otp-input>, because each field reads its slot, focus, and highlight state from the parent OTP input.
Source
Thrown at packages/vuetify/src/components/VOtpInput/VOtpField.tsx:27
import { VOtpInputSymbol } from './shared'
export const makeVOtpFieldProps = propsFactory({
index: {
type: Number,
required: true,
},
}, 'VOtpField')
export const VOtpField = genericComponent()({
name: 'VOtpField',
props: makeVOtpFieldProps(),
setup (props) {
const otpInput = inject(VOtpInputSymbol)
if (!otpInput) {
throw new Error('[Vuetify] VOtpField must be used inside VOtpInput')
}
const slot = computed(() => otpInput.otpSlots.value[props.index])
const isHighlighted = computed(() =>
otpInput.isFocused.value && otpInput.focusAll.value && !slot.value?.isActive
)
useRender(() => {
if (!slot.value) return createCommentVNode()
return (
<VField
data-otp-index={ props.index }
focused={ slot.value.isActive || isHighlighted.value }
class={{ 'v-otp-input__field--highlighted': isHighlighted.value }}
onMousedown={ (e: MouseEvent) => e.preventDefault() }
onClick={ () => otpInput.focusAt(props.index) }View on GitHub (pinned to 8d153908df)
Solutions
- Use <v-otp-field> only as a child of <v-otp-input> (normally you just configure <v-otp-input> and let it render its fields).
- If you need a custom layout, keep all fields inside one <v-otp-input> provide tree.
- In tests, wrap the field in <v-otp-input>.
Example fix
// before <v-otp-field /> // after <v-otp-input /> <!-- let v-otp-input render its fields internally -->
Defensive patterns
Strategy: type-guard
Validate before calling
import { inject } from 'vue'
import { VOtpInputSymbol } from 'vuetify'
const otp = inject(VOtpInputSymbol, null)
if (!otp) {
// not inside <v-otp-input>: render a plain text field instead
} Type guard
import { inject } from 'vue'
import { VOtpInputSymbol } from 'vuetify'
export function isInsideOtpInput (): boolean {
return inject(VOtpInputSymbol, null) != null
} Prevention
- Render <v-otp-field> only inside <v-otp-input>.
- Prefer configuring <v-otp-input> over assembling fields manually.
- Wrap fields in <v-otp-input> for tests.
When it happens
Trigger: Rendering <v-otp-field> as a standalone component; placing it outside <v-otp-input>; composing a custom OTP layout that loses the parent.
Common situations: Trying to lay out OTP fields manually with <v-otp-field>; copy-pasting a field out of an input; testing the field alone.
Related errors
- [Vuetify] v-expansion-panel-text needs to be placed inside v
- [Vuetify] v-expansion-panel-title needs to be placed inside
- [Vuetify] v-slider-thumb must be used inside v-slider or v-r
- [Vuetify] v-slider-track must be inside v-slider or v-range-
- [Vuetify] VWindowItem must be used inside VWindow
AI-assisted analysis of vuetifyjs/vuetify@8d153908df (2026-08-12).
Data as JSON: /api/errors/0a04658d26a7ce0c.
Report an issue: GitHub.