Please fill out this quick survey!
Get resources for workshop:
What's my experience?
Vue Core Team
Vue Mastery Instructor
DX Engineer at Cypress.io
1. Learn
2. Question
3. Apply
1. Learn
2. Question
3. Apply
concepts
examples
stories
clarification
what-ifs
code
experiment
one-on-one help
Scalable Prop Patterns GitHub Repo
Your own projects!
"Raise your hand"
for questions at any time!
All examples are public.
(no need to copy down code examples)
Please do not record
(out of respect for the privacy of participants)
Props are custom attributes you can register on a component.
Props allow us to pass data
into a component.
Array Syntax
<script>
export default {
props: [
'title',
'author',
'genre'
]
}
</script>
Great for prototyping,
but not very helpful otherwise...
Prop Types
Allows you to define basic validation for your props
Common Prop Types
Lesser Known
Array Syntax
<script>
export default {
props: [
'title',
'author',
'genre'
]
}
</script>
Adding a Prop Type
<script>
export default {
props: {
title: String,
author: String,
genre: String
}
}
</script>
Adding Prop Types
<script>
export default {
props: {
title: String,
author: [String, Object],
genre: [String, Array]
}
}
</script>
Is the prop important?
Is the prop important?
<script>
export default {
props: {
title: {
type: String,
},
author: {
type: [String, Object],
},
genre: {
type: [String, Array],
}
}
}
</script>
<script>
export default {
props: {
title: {
type: String,
required: true
},
author: {
type: [String, Object],
required: true
},
genre: {
type: [String, Array],
required: false
}
}
}
</script>
Is the prop important?
How will the prop be used most of the time?
<script>
export default {
props: {
title: {
type: String,
required: true
},
author: {
type: [String, Object],
required: true
},
genre: {
type: [String, Array],
required: false
}
}
}
</script>
How will the prop be used most of the time?
<script>
export default {
props: {
title: {
type: String,
required: true
},
author: {
type: [String, Object],
required: true
},
genre: {
type: [String, Array],
required: false,
default: 'Uncategorized'
}
}
}
</script>
How will the prop be used most of the time?
<script>
export default {
props: {
title: {
type: String,
required: true
},
author: {
type: [String, Object],
required: true
},
genre: {
type: [String, Array],
required: false,
default: 'Uncategorized'
}
}
}
</script>
How will the prop be used most of the time?
<script>
export default {
props: {
title: {
type: String,
required: true
},
author: {
type: [String, Object],
required: true,
default: { name: 'Unknown' }
},
genre: {
type: [String, Array],
required: false,
default: 'Uncategorized'
}
}
}
</script>
How will the prop be used most of the time?
<script>
// Dice.vue
export default {
props: {
colors: {
type: Array,
},
currentValue: {
type: Number,
default: 1
},
material: {
type: Object
}
}
}
</script>
<script>
// Dice.vue
export default {
props: {
colors: {
type: Array,
default: () => ([])
},
currentValue: {
type: Number,
default: 1
},
material: {
type: Object,
default: () => ({})
}
}
}
</script>
Use "factory" functions to generate arrays and objects
<script>
// Dice.vue
export default {
props: {
colors: {
type: Array,
default: () => ([])
},
currentValue: {
type: Number,
default: () => {
return Math.floor(Math.random() * 6 + 1)
}
},
material: {
type: Object,
default: () => ({})
}
}
}
</script>
You can generate dynamic default values!
<script>
export default {
props: {
title: {
type: String,
required: true,
},
author: {
type: [String, Object],
required: true,
default: { name: 'Unknown' }
},
genre: {
type: [String, Array],
required: false,
default: 'Uncategorized'
}
}
}
</script>
How will the prop be used most of the time?
<script>
export default {
props: {
title: {
type: String,
required: true,
},
author: {
type: [String, Object],
required: true,
default: () => ({ name: 'Unknown' })
},
genre: {
type: [String, Array],
required: false,
default: 'Uncategorized'
}
}
}
</script>
How will the prop be used most of the time?
<script>
export default {
props: {
title: {
type: String,
required: true,
},
author: {
type: [String, Object],
required: true,
default: () => ({})
},
genre: {
type: [String, Array],
required: false,
default: 'Uncategorized'
}
}
}
</script>
<script>
export default {
props: {
title: {
type: String,
required: true,
},
author: {
type: [String, Object],
required: true,
default: () => ({})
},
genre: {
type: [String, Array],
required: false,
default: 'Uncategorized'
}
}
}
</script>
<script>
export default {
props: {
title: {
type: String,
required: true,
},
author: {
type: [String, Object],
default: () => ({})
},
genre: {
type: [String, Array],
required: false,
default: 'Uncategorized'
}
}
}
</script>
💡Tip: You just need one
<script>
export default {
props: {
title: {
type: String,
required: true,
},
author: {
type: [String, Object],
default: () => ({})
},
genre: {
type: [String, Array],
default: 'Uncategorized'
}
}
}
</script>
Object Syntax
Array Syntax
<script>
export default {
props: [
'title',
'author',
'genre'
]
}
</script>
<script>
export default {
props: {
title: {
type: String,
required: true,
},
author: {
type: [String, Object],
default: () => ({})
},
genre: {
type: [String, Array],
default: 'Uncategorized'
}
}
}
</script>
Props are custom attributes you can register on a component.
Props allow us to pass data
into a component.
Props are custom attributes you can register on a component.
Props allow us to pass data
into a component.
Props provides guidance for future developers
This is my info alert box
This is my info alert box
<script>
// AlertBox.vue
export default {
props: {
type: {
type: String,
default: 'info'
}
}
}
</script>
<template>
<aside class="alert-box" :class="`is-${type}`">
{{ text }}
</aside>
</template>
This is my info alert box
This is my success alert box
This is my warning alert box
This is my danger alert box
This is my info alert box
This is my success alert box
This is my warning alert box
This is my danger alert box
<template>
<AlertBox type="info" />
<AlertBox type="success" />
<AlertBox type="warning" />
<AlertBox type="danger" />
</template>
This is my info alert box
<script>
// AlertBox.vue
export default {
props: {
type: {
type: String,
default: 'info'
}
}
}
</script>
<template>
<aside class="alert-box" :class="`is-${type}`">
{{ text }}
</aside>
</template>
This is my info alert box
<script>
// AlertBox.vue
export default {
props: {
type: {
type: String,
default: 'info'
}
}
}
</script>
This is my info alert box
<script>
// AlertBox.vue
export default {
props: {
type: {
type: String,
default: 'info',
validator: (propValue) => {
return propValue.length > 0
}
}
}
}
</script>
<script>
export default {
validator: (propValue) => {
return propValue.length > 0
}
}
</script>
<script>
export default {
validator: (propValue) => {
return propValue.length > 0
}
}
</script>
<script>
export default {
validator: (propValue) => {
return propValue.length > 0
}
}
</script>
This is my info alert box
<script>
// AlertBox.vue
export default {
props: {
type: {
type: String,
default: 'info',
validator: (propValue) => {
return propValue.length > 0
}
}
}
}
</script>
This is my info alert box
<script>
// AlertBox.vue
export default {
props: {
type: {
type: String,
default: 'info',
validator: (propValue) => {
const validTypes = ['info', 'success', 'warning', 'danger']
return validTypes.indexOf(propValue) > -1
}
}
}
}
</script>
<script>
// BaseLink.vue
export default {
props: {
href: {
type: String,
default: '',
},
allowInsecure: {
type: Boolean,
default: false,
}
}
</script>
<script>
// BaseLink.vue
export default {
props: {
href: {
type: String,
default: '',
},
allowInsecure: {
type: Boolean,
default: false,
}
},
created() {
this.validateProps()
}
}
</script>
<script>
// BaseLink.vue
export default {
props: {
href: {
type: String,
default: '',
},
allowInsecure: {
type: Boolean,
default: false,
}
},
created() {
this.validateProps()
},
methods: {
validateProps() {
}
}
}
</script>
<script>
// BaseLink.vue
export default {
props: {
href: {
type: String,
default: '',
},
allowInsecure: {
type: Boolean,
default: false,
}
},
created() {
this.validateProps()
},
methods: {
validateProps() {
if (this.href) {
if (!this.allowInsecure && !/^(https|mailto|tel):/.test(this.href)) {
return console.warn(
`Insecure <BaseLink> href: ${this.href}.\n.
If this site does not offer SSL, explicitly add the
allow-insecure attribute on <BaseLink>.`
)
}
}
}
}
}
</script>
<script>
// BaseLink.vue
export default {
props: {
href: {
type: String,
default: '',
},
allowInsecure: {
type: Boolean,
default: false,
}
},
created() {
this.validateProps()
},
methods: {
validateProps() {
if (process.env.NODE_ENV === 'production') return
if (this.href) {
if (!this.allowInsecure && !/^(https|mailto|tel):/.test(this.href)) {
return console.warn(
`Insecure <BaseLink> href: ${this.href}.\n.
If this site does not offer SSL, explicitly add the
allow-insecure attribute on <BaseLink>.`
)
}
}
}
}
}
</script>
<!-- MyLibraryPage.vue -->
<template>
<Library :preferences="preferences">
<Bookshelf v-for="bookshelf in bookshelves" :key="bookshelf.id" :preferences="preferences">
<Book v-for="book in bookshelf.books" :key="book.id" :preferences="preferences">
<Page v-for="page in book.pages" :key="page.id" :preferences="preferences" />
</Book>
</Bookshelf>
</Library>
</template>
<!-- MyLibraryPage.vue -->
<template>
<Library :preferences="preferences">
<Bookshelf v-for="bookshelf in bookshelves" :preferences="preferences">
<Book v-for="book in bookshelf.books" :preferences="preferences">
<Page v-for="page in book.pages" :preferences="preferences" />
</Book>
</Bookshelf>
</Library>
</template>
<!-- MyLibraryPage.vue -->
<template>
<Library :preferences="preferences">
<Bookshelf v-for="bookshelf in bookshelves" :preferences="preferences">
<Book v-for="book in bookshelf.books" :preferences="preferences">
<Page v-for="page in book.pages" :preferences="preferences" />
</Book>
</Bookshelf>
</Library>
</template>
This is typically a sign to refactor your props.
Leverages the computed property to allow you to take props and either break them down or add complexity
<script>
export default {
name: 'BaseDateLabel',
props: {
isoDate: {
/** ISO-8601 format **/
type: String,
required: true
}
}
}
</script>
<template>
<p>
{{ new Date(isoDate) }}
</p>
</template>
<script>
export default {
name: 'BaseDateLabel',
props: {
isoDate: {
/** ISO-8601 format **/
type: String,
required: true
}
},
computed: {
date() {
return new Date(this.isoDate)
}
}
}
</script>
<template>
<p>
{{ date }}
</p>
</template>
<script>
export default {
name: 'BaseDateLabel',
props: {
isoDate: {
/** ISO-8601 format **/
type: String,
required: true
}
},
computed: {
date() {
return new Date(this.isoDate)
}
}
}
</script>
<template>
<p>
{{ date.getFullYear() }}-{{ date.getMonth() + 1 }}-{{ date.getDate() }}
</p>
</template>
<script>
export default {
name: 'BaseDateLabel',
props: {
isoDate: {
/** ISO-8601 format **/
type: String,
required: true
}
},
computed: {
date() {
return new Date(this.isoDate)
},
simpleDate() {
return `${this.date.getFullYear()}-${this.date.getMonth() + 1}-${this.date.getDate()}`
}
}
}
</script>
<template>
<p>
{{ simpleDate }}
</p>
</template>
<script>
export default {
name: 'BaseDateLabel',
props: {
isoDate: {
/** ISO-8601 format **/
type: String,
required: true
}
},
computed: {
date() {
return new Date(this.isoDate)
},
simpleDate() {
return `${this.date.getFullYear()}-${this.date.getMonth() + 1}-${this.date.getDate()}`
},
dayNumber() {
return this.date.getDate()
},
monthNumber() {
return this.date.getMonth() + 1
},
yearNumber() {
return this.date.getFullYear()
},
}
}
</script>
<template>
<p>
{{ simpleDate }}
</p>
</template>
<script>
export default {
name: 'BaseDateLabel',
props: {
isoDate: {
/** ISO-8601 format **/
type: String,
required: true
}
},
computed: {
date() {
return new Date(this.isoDate)
},
simpleDate() {
return `${this.yearNumber}-${this.monthNumber}-${this.dayNumber}`
},
dayNumber() {
return this.date.getDate()
},
monthNumber() {
return this.date.getMonth() + 1
},
yearNumber() {
return this.date.getFullYear()
},
}
}
</script>
<template>
<p>
{{ simpleDate }}
</p>
</template>
<script>
// Counter.vue
export default {
props: {
startingValue: Number
},
methods: {
incrementValue() {
this.startingValue++
}
}
}
</script>
<template>
<div class="counter">
<p>{{ startingValue }}
<button @click="incrementValue">Increase by 1</button>
</div>
</template>
<script>
// Counter.vue
export default {
props: {
startingValue: Number
},
methods: {
incrementValue() {
this.startingValue++
}
}
}
</script>
<template>
<div class="counter">
<p>{{ startingValue }}</p>
<button @click="incrementValue">Increase by 1</button>
</div>
</template>
<script>
// Counter.vue
export default {
props: {
startingValue: Number
},
data: () => ({
currentValue: 0
}),
methods: {
incrementValue() {
this.currentValue++
}
},
created() {
this.currentValue = this.startingValue
}
}
</script>
<template>
<div class="counter">
<p>{{ startingValue }}</p>
<button @click="incrementValue">Increase by 1</button>
</div>
</template>
<script>
// Book.vue
export default {
props: {
title: String,
author: [Array, String],
publishDate: [Date, String],
publisher: [Object, String],
length: Number,
editions: [Array, Number],
genre: [Array, String],
isbn: String,
vendors: Array,
editor: [Object, String],
website: String
}
}
</script>
<script>
// Book.vue
export default {
props: {
author: [Array, String],
editions: [Array, Number],
editor: [Object, String],
genre: [Array, String],
isbn: String,
length: Number,
publishDate: [Date, String],
publisher: [Object, String],
title: String,
vendors: Array,
website: String,
},
}
</script>
<script>
// Book.vue
export default {
props: {
author: [Array, String],
editions: [Array, Number],
editor: [Object, String],
genre: [Array, String],
isbn: String,
length: Number,
publishDate: [Date, String],
publisher: [Object, String],
title: String,
vendors: Array,
website: String,
},
}
</script>
<script>
// Book.vue
export default {
props: {
author: [Array, String],
editions: [Array, Number],
editor: [Object, String],
genre: [Array, String],
isbn: String,
/** Number of pages in the printed edition **/
length: Number,
publishDate: [Date, String],
publisher: [Object, String],
title: String,
vendors: Array,
website: String,
},
}
</script>
<script>
// Book.vue
export default {
props: {
author: [Array, String],
editions: [Array, Number],
editor: [Object, String],
genre: [Array, String],
isbn: String,
length: Number,
publishDate: [Date, String],
publisher: [Object, String],
title: String,
vendors: Array,
website: String,
}
}
</script>
<script>
// Book.vue
export default {
props: {
data: {
type: Object,
required: true
}
}
}
</script>
<script>
// Book.vue
export default {
props: {
data: {
type: Object,
required: true
}
},
data: () => ({
// Local data store
})
}
</script>
<script>
// Book.vue
export default {
props: {
data: {
type: Object,
required: true
}
},
data: () => ({
// Local data store
})
}
</script>
In addition to data, some other prop names to be cautious of include:
<script>
export default {
name: 'BaseButton',
props: {
className: {
type: Array
},
text: {
type: String,
default: 'Submit'
}
},
};
</script>
<template>
<button :class="['button', ...className]">
{{ text }}
</button>
</template>
<template>
<BaseButton :className="['is-primary', 'is-outlined']" />
</template>
<script>
export default {
name: 'BaseButton',
props: {
className: {
type: Array
},
text: {
type: String,
default: 'Submit'
}
},
};
</script>
<template>
<button :class="['button', ...className]">
{{ text }}
</button>
</template>
<script>
export default {
name: 'BaseButton',
props: {
text: {
type: String,
default: 'Submit'
}
},
};
</script>
<template>
<button class="button">
{{ text }}
</button>
</template>
<template>
<BaseButton class="is-primary is-outlined" />
</template>
<button class="button is-primary is-outlined">
Submit
</button>
<script>
export default {
name: "ComponentLibraryButton",
props: {
className: {
type: String,
default: "button is-primary"
},
text: {
type: String,
default: "Submit"
}
}
}
</script>
<template>
<button :class="className">
{{ text }}
</button>
</template>
https://bencodezen.typeform.com/to/qktOXj