--- name: component-props description: Declare and use props with type safety and validation --- # Component Props Props are custom attributes for passing data from parent to child components. ## Type-Based Declaration (Recommended) Use TypeScript interfaces with `defineProps`: ```vue ``` ## Reactive Props Destructure (3.5+) Destructure props while keeping reactivity: ```vue ``` ### Passing destructured props to functions ```ts const { foo } = defineProps(['foo']) // ❌ Won't work - passes value not reactive source watch(foo, /* ... */) // ✅ Wrap in getter watch(() => foo, /* ... */) // ✅ For composables useComposable(() => foo) ``` ## Default Values with withDefaults (3.4 and below) ```vue ``` ## Runtime Declaration For runtime validation without TypeScript: ```vue ``` ## Prop Types Supported `type` values: - `String`, `Number`, `Boolean`, `Array`, `Object` - `Date`, `Function`, `Symbol`, `Error` - Custom classes (uses `instanceof`) ```ts class Person { constructor(public name: string) {} } defineProps({ author: Person // instanceof check }) ``` ## Nullable Props ```ts defineProps({ id: { type: [String, null], required: true } }) ``` ## Boolean Casting Boolean props have special casting rules: ```vue ``` ## One-Way Data Flow Props form a one-way binding: parent → child. Never mutate props directly. ```ts const props = defineProps(['initialCounter']) // ❌ Don't mutate props props.initialCounter = 5 // ✅ Use local state initialized from prop const counter = ref(props.initialCounter) // ✅ Or use computed for transformations const normalizedSize = computed(() => props.size.trim().toLowerCase()) ``` ## Passing Props ```vue-html ``` ## Prop Name Casing - Declare with camelCase in JavaScript - Use kebab-case in templates ```ts defineProps({ greetingMessage: String }) ``` ```vue-html ```