skip to content

In Vue 3, how does Boolean casting resolve a prop typed Boolean when the parent omits it, writes it bare, or passes a string?

level: middleimportance: should knowfreq 50%

answer

  1. mimics native boolean attributes
  2. absent is not undefined
  3. empty string becomes true
  4. String before Boolean changes it
  5. quoted 'false' is a string

basics

~20 s

A prop whose type includes Boolean is cast: absent becomes false instead of undefined unless a default is declared, and a bare attribute or a value equal to the kebab-case prop name becomes true. Any other string, including "false", is passed as that string.

solid answer

~30 s

Vue casts props whose `type` includes `Boolean` to mimic native boolean attributes. For `defineProps({ disabled: Boolean })`, `<UserCard disabled />` passes `''`, which Vue turns into `true`; `disabled="disabled"` also becomes `true`; omitting the attribute gives `false`, not `undefined`, unless the prop declares a `default`. Only the empty string and the prop's own kebab-case name are cast, so `disabled="false"` passes the truthy string `'false'` and earns a dev type-check warning; you bind `:disabled="false"` instead. With multiple types, the empty-string-to-`true` cast is skipped when `String` appears before `Boolean`, so `[String, Boolean]` leaves a bare attribute as `''`.

code

vue · 13 lines
vue
<script setup>
const props = defineProps({
  disabled: Boolean,
  highlighted: { type: Boolean, default: undefined }
})
</script>

<template>
  <article :class="{ muted: props.disabled }">
    <span v-if="props.highlighted === undefined">default style</span>
    <slot />
  </article>
</template>

go deeper

for a junior

Remember that a bare attribute means true and an omitted Boolean prop means false.

for a middle

Explain the exact cast cases, the String-before-Boolean ordering rule, and why default: undefined keeps absent as undefined.

for a senior

Diagnose the quoted 'false' bug quickly and note that production builds give no warning for it.

for a principal

Set a component-library rule for flag props, such as pure Boolean types and explicit tri-state defaults, so consumers get predictable behaviour.

## Why Vue casts Boolean props Native HTML has **boolean attributes**: `<button disabled>` means disabled, and the attribute's text value is irrelevant. Vue 3 copies that feel for component props through **Boolean casting**, applied while it resolves a component's props. It kicks in for any prop whose declared `type` is `Boolean` or an array containing `Boolean`, including props compiled from a type-based declaration such as `disabled?: boolean`. Without casting, `<UserCard disabled />` would hand the child an empty string, and an omitted prop would be `undefined`. Casting turns both into the booleans you meant. ## The rules for a plain Boolean prop Given `defineProps({ disabled: Boolean })` on a `<UserCard>`: | Parent writes | `props.disabled` | Why | |---|---|---| | `<UserCard />` | `false` | absent and no `default`, so cast to `false` | | `<UserCard disabled />` | `true` | empty string is cast to `true` | | `<UserCard disabled="disabled" />` | `true` | value equals the hyphenated prop name | | `<UserCard :disabled="false" />` | `false` | a real boolean passes through | | `<UserCard disabled="false" />` | `'false'` | an arbitrary string is not cast | The last row is the classic bug. A static attribute always passes a string, and the string `'false'` is **truthy**, so `v-if="disabled"` in the child renders the disabled branch. In a development build Vue also logs a type-check warning because a string failed a `Boolean` check, but production builds skip validation and stay silent. ## Absent means false, unless you declare a default For ordinary props an absent optional prop is `undefined`. Boolean props are the exception: the runtime casts **absent without a default** to `false`. Two consequences follow: 1. A child cannot tell "parent said false" from "parent said nothing" by looking at the value. 2. If you need a three-state input (on, off, not specified), declare `default: undefined`. Having any `default` key disables the absent-to-`false` cast, so the value stays `undefined`. A `default: true` works the same way: an absent prop resolves to `true`, while `:disabled="false"` still passes `false`. ## Multiple types and the String ordering edge When `type` is an array, the Boolean rules still apply, with one twist around `String`: - `[Boolean, String]`, `[Boolean, Number]`, `[Number, Boolean]` — a bare attribute becomes `true`. - `[String, Boolean]` — `String` appears before `Boolean`, so the empty-string cast is turned off and a bare `<UserCard label />` passes `''`. In the runtime this is one flag per prop: whether to cast at all (any `Boolean` in the list) and whether to cast the empty string to `true` (no `String` ahead of `Boolean`). The absent-to-`false` cast still applies to `[String, Boolean]`, which surprises people who expected `undefined`. ## Practical guidance - Bind non-literal booleans with `:`; write the bare attribute only for `true`. - Keep flags as pure `Boolean` unless you deliberately accept text too. - When a flag must distinguish "unset", use `default: undefined` and document it. - In type-based declarations, `disabled?: boolean` is still cast: absent is `false`, not `undefined`, which breaks code that checks `props.disabled === undefined`. ## Common misreadings - **"Any present attribute is true."** Only `''` and the prop's own kebab-case name are cast; other strings pass through unchanged. - **"Vue parses 'false' for you."** It does not; there is no string-to-boolean parsing beyond the two cast cases. - **"Boolean casting applies to any prop."** It applies only when `Boolean` is among the declared types; an undeclared attribute is not a prop at all and falls through as an attribute. ## Debugging a flag that looks wrong When a card renders as disabled although the parent "passed false", check in this order: 1. **Is the binding static?** `disabled="false"` is a string; the fix is `:disabled="false"` or a bound variable. 2. **Is the prop declared as `Boolean`?** An undeclared `disabled` is not a prop at all, so no casting happens and it falls through as an attribute. 3. **Does the type list put `String` first?** Then a bare attribute arrives as `''`, which is falsy in `v-if`. 4. **Is there a `default`?** A `default: true` makes an absent flag resolve to `true`. Running the development build surfaces the first case as a type-check warning; production gives you only the wrong rendering.

  • A Vue 3 `<UserCard>` declares `disabled: Boolean` and receives `disabled="false"` from a template. What does the child see in production?
    The string `'false'`. Vue only casts an empty string or the value `'disabled'` to `true`; every other string passes through. The string is truthy, so the card renders as disabled, and because prop validation is development-only there is no warning in production. The fix is `:disabled="false"`.
  • How do you let a Vue 3 Boolean prop distinguish 'not passed' from 'passed false'?
    Give it `default: undefined`. The runtime casts an absent Boolean prop to `false` only when no `default` is declared, so declaring one, even `undefined`, leaves an absent prop as `undefined` while `:disabled="false"` still yields `false`.

It works like a light switch labelled only 'on': flipping it present means on, leaving it untouched means off, and writing the word 'off' on the label does not turn it off.

saying these in an interview costs you the question

  • Expects an omitted Boolean prop to be undefined
  • Thinks disabled="false" passes the boolean false
  • Believes any present attribute value is cast to true
  • Assumes [String, Boolean] still turns a bare attribute into true
  • Thinks disabled?: boolean in a type declaration skips casting