Skip to content
Copied!
published on 2026-04-05

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.js

To register them as global components, add the following to .vitepress/theme/index.js. After this, the components are available in every Markdown file.

.vitepress/theme/index.js
js
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:

.vitepress/theme/components/Hello.vue
html
<template>
  <p>hello, world</p>
</template>

Create .vitepress/theme/index.ts with the following content:

.vitepress/theme/index.ts
ts
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:

html
<hello />

After reloading VitePress, this section renders as:

hello, world

In 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:

html
<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:

.vitepress/theme/components/Hello.vue
html
<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:

html
<hello name="John" />

After reloading, this renders as:

hello, John

The 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?

html
<hello name="John" />
<hello />

This renders as:

plain
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:

.vitepress/theme/components/Hello.vue
html
<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:

plain
hello, John
hello, world

Default Value via defineProps

defineProps accepts an object for detailed validation options. The following applies the default 'world' when name is not supplied:

html
<template>
  <p>hello, {{ name }}</p>
</template>

<script setup>
defineProps({
  name: { type: String, default: 'world' }
})
</script>
html
<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.