Next.js starter your AI actually understands. Ship internal tools in days not weeks. Pre-order $199 $499 → [Get it now]

How to define props in Vue 3 with defineProps

defineProps is the <script setup> compiler macro for declaring component props in Vue 3, replacing the props option from the Options API with a more concise and TypeScript-friendly syntax. As the creator of CoreUI with Vue development experience since 2014, I use defineProps in every <script setup> component because it gives both runtime validation and full TypeScript type inference without any extra configuration. Choosing between the runtime syntax and the TypeScript generic syntax depends on whether you need complex default values — the TypeScript syntax offers better type safety but requires withDefaults for defaults. Both approaches are idiomatic and supported — choose the one that fits your project’s TypeScript configuration.

Declare props with runtime validation syntax.

<script setup>
import { computed } from 'vue'

const props = defineProps({
  title: {
    type: String,
    required: true
  },
  count: {
    type: Number,
    default: 0
  },
  variant: {
    type: String,
    default: 'primary',
    validator: (val) => ['primary', 'secondary', 'danger'].includes(val)
  },
  items: {
    type: Array,
    default: () => []
  }
})

const label = computed(() => `${props.title} (${props.count})`)
</script>

<template>
  <div :class="`card card--${variant}`">
    <h3>{{ label }}</h3>
    <ul>
      <li v-for="item in items" :key="item.id">{{ item.name }}</li>
    </ul>
  </div>
</template>

validator is a function that returns true for valid values. Vue logs a warning in development when an invalid value is passed. Note that Array and Object defaults must be factory functions (() => []) to avoid sharing the same instance across components.

TypeScript Generic Syntax

Use TypeScript interfaces for full type safety.

<script setup lang="ts">
import { computed } from 'vue'

interface User {
  id: number
  name: string
  email: string
}

interface Props {
  user: User
  isSelected?: boolean
  size?: 'sm' | 'md' | 'lg'
}

const props = withDefaults(defineProps<Props>(), {
  isSelected: false,
  size: 'md'
})

const displayName = computed(() => props.user.name.split(' ')[0])
</script>

defineProps<Props>() infers types from the TypeScript interface. withDefaults adds default values — required when using the TypeScript syntax because TypeScript interfaces cannot define runtime defaults directly. Props without defaults are required.

Accessing Props in Templates

Props are available directly in the template without props. prefix.

<template>
  <!-- Access props directly in template -->
  <div>
    <p>{{ title }}</p>
    <p>{{ count }}</p>

    <!-- But in script, use props.title -->
  </div>
</template>

<script setup>
const props = defineProps({ title: String, count: Number })

// In script: always use props.title, props.count
console.log(props.title)
</script>

Vue automatically unwraps defineProps return values in the template. In <script setup>, always access props through the returned object (props.title) to maintain reactivity.

Best Practice Note

In CoreUI Vue components we use the TypeScript generic syntax (defineProps<Props>()) for all typed components because it provides better IDE autocompletion, stricter type checking, and eliminates the need to type both the validation object and the TypeScript interface separately. See how to define emits in Vue 3 with defineEmits for the companion emit declaration pattern.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.

Answers by CoreUI Core Team