Vue Components
VitePress lets you write <script> and <style> tags directly in Markdown files. You can also define reusable components that combine HTML, CSS, and JavaScript ahead of time, then call them from Markdown. This system is called Vue Single-File Components. Once a Vue component is defined, it can be used as a shared building block across multiple Markdown files.
Directory Structure and Global Components
Vue components go in .vitepress/components. You can place multiple components there.
.vitepress
├── config.mts
└── theme
├── components
│ ├── Component1.vue
│ ├── Component2.vue
│ └── Component3.vue
└── index.jsTo register them as global components, add the following to .vitepress/theme/index.js. After this, the components are available in every Markdown file.
import DefaultTheme from 'vitepress/theme'
import Component1 from './components/Component1.vue'
import Component2 from './components/Component2.vue'
import Component3 from './components/Component3.vue'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.component('Component1', Component1)
app.component('Component2', Component2)
app.component('Component3', Component3)
}
}"hello, world"
First, let's create a Vue component that replaces <hello /> in Markdown with hello, world.
Create .vitepress/theme/components/Hello.vue with the following content:
<template>
<p>hello, world</p>
</template>Create .vitepress/theme/index.ts with the following content:
import DefaultTheme from 'vitepress/theme'
import Hello from './components/Hello.vue'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.component('Hello', Hello)
}
}Then write the following in a Markdown file:
<hello />After reloading VitePress, this section renders as:
hello, worldIn a Vue component, everything inside the <template> tag describes the HTML that is inserted into the VitePress document's DOM. In this example that is:
<template>
<p>hello, world</p>
</template>This replaces the <hello /> tag in Markdown with <p>hello, world</p> in the DOM.
"hello, NAME"
Without a Default Value
In the example above, the component always outputs fixed HTML. Now let's pass the "world" part as a prop.
The .vitepress/theme/index.ts file is unchanged from the hello world example and is omitted below.
Update .vitepress/theme/components/Hello.vue as follows:
<template>
<p>hello, world</p>
<p>hello, {{ name }}</p>
</template>
<script setup>
defineProps({
name: String
})
</script>In a Vue component, everything inside the <script setup> tag is interpreted and executed as JavaScript. Variables defined here can be interpolated in <template> using mustaches.
defineProps() configures how props passed from VitePress are received. Here name: String declares that the name attribute must be of type String. This type-checking mechanism is called validation. Beyond type checking, you can also mark props as required (required) or provide custom validators.
Write the following in a Markdown file:
<hello name="John" />After reloading, this renders as:
hello, JohnThe name="John" attribute is passed to the component, assigning "John" to the name variable. The placeholder in <template> is then replaced with John, and <p>hello, John</p> is inserted into the DOM.
What happens if the name attribute is omitted?
<hello name="John" />
<hello />This renders as:
hello, John
hello,When the name attribute is absent, no error is thrown — it simply substitutes an empty string. To avoid unexpectedly odd output, you can set a default value.
Default Value via Mustache Template
In mustaches you can specify a default using ||. For example, the following uses 'world' when name is undefined:
<template>
<p>hello, {{ name }}</p>
<p>hello, {{ name || 'world' }}</p>
</template>
<script setup>
defineProps({
name: String
})
</script>After reloading, the earlier example now renders as:
hello, John
hello, worldDefault Value via defineProps
defineProps accepts an object for detailed validation options. The following applies the default 'world' when name is not supplied:
<template>
<p>hello, {{ name }}</p>
</template>
<script setup>
defineProps({
name: { type: String, default: 'world' }
})
</script><template>
<p>hello, {{ name || 'world' }}</p>
<p>hello, {{ name }}</p>
</template>
<script setup>
defineProps({
name: String
name: { type: String, default: 'world' }
})
</script>Validation
The full list of validation options for defineProps is documented at Props.