with one click
slidev-components
// Leverage Vue components in Slidev slides. Use this skill to add interactivity with built-in components or create custom ones.
// Leverage Vue components in Slidev slides. Use this skill to add interactivity with built-in components or create custom ones.
| name | slidev-components |
| description | Leverage Vue components in Slidev slides. Use this skill to add interactivity with built-in components or create custom ones. |
This skill covers using Vue components in Slidev, including all built-in components and how to create custom interactive elements for your presentations.
Components can be used directly in Markdown:
# My Slide
<MyComponent />
<Counter :start="5" />
Draws an arrow between points.
<Arrow x1="10" y1="20" x2="100" y2="200" />
<Arrow
x1="10"
y1="20"
x2="100"
y2="200"
color="#f00"
width="3"
/>
Props:
x1, y1: Start coordinatesx2, y2: End coordinatescolor: Arrow colorwidth: Line widthDraggable arrow (useful for presentations).
<VDragArrow />
Automatically adjusts font size to fit container.
<AutoFitText :max="200" :min="50" modelValue="My Text" />
Props:
max: Maximum font sizemin: Minimum font sizemodelValue: Text contentRenders different content based on theme.
<LightOrDark>
<template #light>
<img src="/logo-dark.png" />
</template>
<template #dark>
<img src="/logo-light.png" />
</template>
</LightOrDark>
Navigation link to other slides.
<Link to="42">Go to slide 42</Link>
<Link to="/intro">Go to intro</Link>
Display slide numbers.
Slide <SlideCurrentNo /> of <SlidesTotal />
Generates a table of contents.
<Toc />
<Toc maxDepth="2" />
<Toc mode="onlyCurrentTree" />
Props:
maxDepth: Maximum heading depthmode: Display mode (all, onlyCurrentTree, onlySiblings)Applies CSS transforms.
<Transform :scale="1.5">
<div>Scaled content</div>
</Transform>
<Transform :scale="0.8" :rotate="10">
Rotated and scaled
</Transform>
Props:
scale: Scale factorrotate: Rotation in degreesEmbeds a tweet.
<Tweet id="1234567890" />
<Tweet id="1234567890" scale="0.8" />
Embeds a YouTube video.
<Youtube id="dQw4w9WgXcQ" />
<Youtube id="dQw4w9WgXcQ" width="560" height="315" />
Props:
id: YouTube video IDwidth, height: DimensionsEmbeds a video file.
<SlidevVideo v-click autoplay controls>
<source src="/video.mp4" type="video/mp4" />
</SlidevVideo>
Props:
autoplay: Auto-play on slide entercontrols: Show video controlsloop: Loop videoConditional rendering based on context.
<RenderWhen context="slide">
Only visible in slide view
</RenderWhen>
<RenderWhen context="presenter">
Only visible in presenter view
</RenderWhen>
Context options: slide, presenter, previewNext, print
Makes elements draggable.
<VDrag>
<div class="p-4 bg-blue-500 text-white">
Drag me!
</div>
</VDrag>
<VDrag :initialX="100" :initialY="50">
Positioned draggable
</VDrag>
Reveals on click.
<v-click>
Revealed on first click
</v-click>
<v-click at="2">
Revealed on second click
</v-click>
Reveals children sequentially.
<v-clicks>
- First item
- Second item
- Third item
</v-clicks>
Props:
depth: Depth for nested listsevery: Items per clickReveals with the previous element.
<v-click>First</v-click>
<v-after>Appears with first</v-after>
Switches between content based on clicks.
<v-switch>
<template #1>Step 1 content</template>
<template #2>Step 2 content</template>
<template #3>Step 3 content</template>
</v-switch>
Create components/Counter.vue:
<script setup>
import { ref } from 'vue'
const props = defineProps({
start: {
type: Number,
default: 0
}
})
const count = ref(props.start)
</script>
<template>
<div class="counter">
<button @click="count--">-</button>
<span class="count">{{ count }}</span>
<button @click="count++">+</button>
</div>
</template>
<style scoped>
.counter {
display: flex;
align-items: center;
gap: 1rem;
}
button {
padding: 0.5rem 1rem;
font-size: 1.5rem;
cursor: pointer;
}
.count {
font-size: 2rem;
min-width: 3rem;
text-align: center;
}
</style>
Usage:
# Interactive Counter
<Counter :start="10" />
<!-- components/Card.vue -->
<script setup>
defineProps({
title: String,
color: {
type: String,
default: 'blue'
}
})
</script>
<template>
<div :class="`card card-${color}`">
<h3 v-if="title">{{ title }}</h3>
<slot />
</div>
</template>
<style scoped>
.card {
padding: 1.5rem;
border-radius: 0.5rem;
margin: 1rem 0;
}
.card-blue { background: #3b82f6; color: white; }
.card-green { background: #22c55e; color: white; }
.card-red { background: #ef4444; color: white; }
</style>
Usage:
<Card title="Important" color="red">
This is a red card with important content.
</Card>
<!-- components/ProgressBar.vue -->
<script setup>
import { computed } from 'vue'
import { useNav } from '@slidev/client'
const { currentSlideNo, total } = useNav()
const progress = computed(() =>
(currentSlideNo.value / total.value) * 100
)
</script>
<template>
<div class="progress-bar">
<div
class="progress"
:style="{ width: `${progress}%` }"
/>
</div>
</template>
<style scoped>
.progress-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 4px;
background: #e5e7eb;
}
.progress {
height: 100%;
background: #3b82f6;
transition: width 0.3s;
}
</style>
<!-- components/CodeDemo.vue -->
<script setup>
import { ref, computed } from 'vue'
const props = defineProps({
code: String,
language: {
type: String,
default: 'javascript'
}
})
const output = ref('')
const error = ref('')
const run = () => {
try {
output.value = eval(props.code)
error.value = ''
} catch (e) {
error.value = e.message
output.value = ''
}
}
</script>
<template>
<div class="code-demo">
<pre><code>{{ code }}</code></pre>
<button @click="run">Run</button>
<div v-if="output" class="output">{{ output }}</div>
<div v-if="error" class="error">{{ error }}</div>
</div>
</template>
Access navigation state:
<script setup>
import { useNav } from '@slidev/client'
const {
currentSlideNo, // Current slide number
total, // Total slides
next, // Go to next
prev, // Go to previous
go // Go to specific slide
} = useNav()
</script>
Access slide context:
<script setup>
import { useSlideContext } from '@slidev/client'
const {
$slidev, // Global context
$clicks, // Current click count
$page // Current page number
} = useSlideContext()
</script>
Appears above all slides:
<!-- global-top.vue -->
<template>
<div class="absolute top-4 right-4">
<img src="/logo.png" class="h-8" />
</div>
</template>
Appears below all slides:
<!-- global-bottom.vue -->
<template>
<footer class="absolute bottom-4 left-4 text-sm opacity-50">
© 2025 My Company
</footer>
</template>
<div class="fixed bottom-4 right-4 text-sm">
<SlideCurrentNo /> / <SlidesTotal />
</div>
<!-- components/SocialLinks.vue -->
<template>
<div class="flex gap-4">
<a href="https://twitter.com/..." target="_blank">
<carbon-logo-twitter class="text-2xl" />
</a>
<a href="https://github.com/..." target="_blank">
<carbon-logo-github class="text-2xl" />
</a>
</div>
</template>
<!-- components/QRCode.vue -->
<script setup>
import { ref, onMounted } from 'vue'
import QRCodeLib from 'qrcode'
const props = defineProps({
url: String,
size: { type: Number, default: 200 }
})
const qrDataUrl = ref('')
onMounted(async () => {
qrDataUrl.value = await QRCodeLib.toDataURL(props.url, {
width: props.size
})
})
</script>
<template>
<img :src="qrDataUrl" :width="size" :height="size" />
</template>
When creating components, provide:
COMPONENT: [name]
PURPOSE: [what it does]
FILE: components/[Name].vue
---
<script setup>
[script content]
</script>
<template>
[template content]
</template>
<style scoped>
[styles]
</style>
---
USAGE IN SLIDES:
```markdown
<[Name] prop="value" />
PROPS:
Master v-click and sequential animations in Slidev. Use this skill to reveal content progressively and create engaging presentations.
Add smooth slide transitions in Slidev. Use this skill for fade, slide, and custom transitions between slides.
Create beautiful code blocks with Shiki syntax highlighting. Use this skill for code snippets, line highlighting, and code groups.
Animate code transformations with Shiki Magic Move. Use this skill to create smooth morphing transitions between code states.
Add live coding with Monaco Editor in Slidev. Use this skill for interactive code demos, executable code, and real-time editing.
Use built-in and custom Slidev layouts effectively. Use this skill to structure slides with cover, two-cols, image layouts and more.