Introduction
Vue validates prop types at runtime and warns when the received type does not match the declared type. A common source of this error is confusing static attributes with dynamic bindings in templates:
[Vue warn]: Invalid prop: type check failed for prop "count". Expected String
with value "42", got Number with value 42.This happens because unbound HTML attributes are always strings, while v-bind (or :) passes the actual JavaScript value.
Symptoms
- Console warning: "Invalid prop: type check failed"
- Prop validation fails but the component still renders with the value
- TypeScript shows type errors that do not match runtime behavior
- Numeric props received as strings or vice versa
- Boolean props not working as expected (e.g.,
disabled="false"is truthy)
Common Causes
- Static attribute passes a string:
count="42"instead of:count="42" - Template interpolation used where v-bind is needed:
count="{{ value }}" - HTML attributes always pass strings, even for numbers and booleans
- Prop defined with wrong type in the component definition
- Third-party component expects a different type than documented
Step-by-Step Fix
- 1.Use v-bind for dynamic values (numbers, booleans, objects, arrays):
- 2.```html
- 3.<!-- BAD: passes string "42" -->
- 4.<Counter count="42" />
<!-- GOOD: passes number 42 --> <Counter :count="42" />
<!-- GOOD: passes a variable --> <Counter :count="userCount" /> ```
- 1.Define props with multiple acceptable types:
- 2.```javascript
- 3.export default {
- 4.props: {
- 5.count: {
- 6.type: [String, Number],
- 7.default: 0,
- 8.validator(value) {
- 9.return value >= 0;
- 10.},
- 11.},
- 12.},
- 13.};
- 14.
` - 15.Coerce prop values internally when you cannot control the parent:
- 16.```javascript
- 17.export default {
- 18.props: {
- 19.count: {
- 20.type: [String, Number],
- 21.default: 0,
- 22.},
- 23.},
- 24.computed: {
- 25.normalizedCount() {
- 26.return Number(this.count);
- 27.},
- 28.},
- 29.};
- 30.
` - 31.Handle boolean props correctly:
- 32.```html
- 33.<!-- BAD: "false" is a truthy string -->
- 34.<Button disabled="false" />
<!-- GOOD: binds the actual boolean --> <Button :disabled="false" /> <Button :disabled="isDisabled" />
<!-- Shorthand for boolean true --> <Button disabled /> ```
- 1.Add a custom prop validator for stricter type checking:
- 2.```javascript
- 3.export default {
- 4.props: {
- 5.status: {
- 6.type: String,
- 7.required: true,
- 8.validator(value) {
- 9.return ['pending', 'active', 'completed', 'cancelled'].includes(value);
- 10.},
- 11.},
- 12.},
- 13.};
- 14.
`
Prevention
- Always use
v-bind(:) for non-string prop values - Define props with explicit types and default values
- Use TypeScript with
vue-tscto catch type mismatches at compile time - Enable
eslint-plugin-vuewith thevalid-define-propsrule - Test component prop validation in unit tests with incorrect types
- Document expected prop types in JSDoc comments for team members
- Use prop validators for enum-like string props to catch typos early
- Run
vue-cli-service lintin CI to catch prop type issues before deployment