Custom Containers
Overview
Custom containers in VitePress allow you to create styled content blocks that stand out from regular text. They're perfect for highlighting important information, warnings, tips, and other special content. This guide covers all aspects of using and customizing containers in VitePress.
Built-in Containers
VitePress provides several built-in container types that you can use immediately.
Basic Containers
::: info
This is an info box.
:::
::: tip
This is a tip.
:::
::: warning
This is a warning.
:::
::: danger
This is a dangerous warning.
:::
::: details
This is a details block that can be toggled open or closed.
:::
Custom Titles
You can customize the title of each container:
::: info Custom Title
This info box has a custom title.
:::
::: tip PRO TIP
This tip has a custom title.
:::
::: warning CAUTION
This warning has a custom title.
:::
::: danger STOP
This dangerous warning has a custom title.
:::
::: details Click me to view more
This details block has a custom title.
:::
Nested Containers
Containers can be nested inside each other:
::: info
This is an info box.
::: tip
This is a tip inside an info box.
:::
:::
Creating Custom Containers
Basic Custom Container
Define custom containers in your VitePress configuration:
// .vitepress/config.js
export default {
markdown: {
config: (md) => {
const { container } = require('markdown-it-container')
md.use(container, 'note', {
render: (tokens, idx) => {
const token = tokens[idx]
if (token.nesting === 1) {
// opening tag
return `<div class="custom-container note">\n<p class="custom-container-title">NOTE</p>\n`
} else {
// closing tag
return '</div>\n'
}
}
})
}
}
}
Then use it in your Markdown:
::: note
This is a custom note container.
:::
Advanced Custom Container
Create more advanced containers with custom rendering logic:
// .vitepress/config.js
export default {
markdown: {
config: (md) => {
const { container } = require('markdown-it-container')
md.use(container, 'theorem', {
validate: function(params) {
return params.trim().match(/^theorem\s+(.*)$/)
},
render: function (tokens, idx) {
const m = tokens[idx].info.trim().match(/^theorem\s+(.*)$/)
if (tokens[idx].nesting === 1) {
// opening tag
return `<div class="custom-container theorem">\n<p class="custom-container-title">Theorem: ${md.utils.escapeHtml(m[1])}</p>\n`
} else {
// closing tag
return '</div>\n'
}
}
})
}
}
}
Use it with a custom title:
::: theorem Pythagorean Theorem
In a right-angled triangle, the square of the hypotenuse is equal to the sum of the squares of the other two sides.
a² + b² = c²
:::
Styling Custom Containers
Basic CSS Styling
Add custom styles for your containers:
/* .vitepress/theme/custom.css */
.custom-container.note {
border-color: #3eaf7c;
background-color: rgba(62, 175, 124, 0.1);
}
.custom-container.note .custom-container-title {
color: #3eaf7c;
}
.custom-container.theorem {
border-color: #6200ee;
background-color: rgba(98, 0, 238, 0.1);
}
.custom-container.theorem .custom-container-title {
color: #6200ee;
}
Import your custom CSS in your theme:
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import './custom.css'
export default {
...DefaultTheme
}
Using CSS Variables
Leverage VitePress CSS variables for consistent styling:
/* .vitepress/theme/custom.css */
:root {
--c-note: #3eaf7c;
--c-note-bg: rgba(62, 175, 124, 0.1);
--c-theorem: #6200ee;
--c-theorem-bg: rgba(98, 0, 238, 0.1);
}
.dark {
--c-note: #5ccb9e;
--c-note-bg: rgba(92, 203, 158, 0.1);
--c-theorem: #b388ff;
--c-theorem-bg: rgba(179, 136, 255, 0.1);
}
.custom-container.note {
border-color: var(--c-note);
background-color: var(--c-note-bg);
}
.custom-container.note .custom-container-title {
color: var(--c-note);
}
.custom-container.theorem {
border-color: var(--c-theorem);
background-color: var(--c-theorem-bg);
}
.custom-container.theorem .custom-container-title {
color: var(--c-theorem);
}
Interactive Containers
Collapsible Containers
Create custom collapsible containers:
// .vitepress/config.js
export default {
markdown: {
config: (md) => {
const { container } = require('markdown-it-container')
md.use(container, 'collapsible', {
render: (tokens, idx) => {
const token = tokens[idx]
const info = token.info.trim().slice('collapsible'.length).trim()
if (token.nesting === 1) {
// opening tag
return `<details class="custom-container collapsible">\n<summary>${info || 'Details'}</summary>\n`
} else {
// closing tag
return '</details>\n'
}
}
})
}
}
}
Use it in your Markdown:
::: collapsible Click to expand
This content is collapsible.
- You can include lists
- And other Markdown elements
:::
Tabbed Containers
Create tabbed containers with a custom component:
<!-- .vitepress/theme/components/Tabs.vue -->
<script setup>
import { ref } from 'vue'
const props = defineProps({
tabs: { type: Array, required: true }
})
const activeTab = ref(0)
</script>
<template>
<div class="tabs">
<div class="tabs-header">
<button
v-for="(tab, i) in tabs"
:key="i"
:class="{ active: activeTab === i }"
@click="activeTab = i"
>
{{ tab.title }}
</button>
</div>
<div class="tabs-content">
<div
v-for="(tab, i) in tabs"
:key="i"
:class="{ active: activeTab === i }"
class="tab-pane"
>
<slot :name="`tab-${i}`"></slot>
</div>
</div>
</div>
</template>
<style scoped>
.tabs {
margin: 1rem 0;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
overflow: hidden;
}
.tabs-header {
display: flex;
border-bottom: 1px solid var(--vp-c-divider);
}
.tabs-header button {
padding: 0.5rem 1rem;
background: transparent;
border: none;
cursor: pointer;
font-size: 0.9rem;
}
.tabs-header button.active {
border-bottom: 2px solid var(--vp-c-brand);
font-weight: bold;
}
.tabs-content {
padding: 1rem;
}
.tab-pane {
display: none;
}
.tab-pane.active {
display: block;
}
</style>
Register it in your theme:
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import Tabs from './components/Tabs.vue'
import './custom.css'
export default {
...DefaultTheme,
enhanceApp({ app }) {
app.component('Tabs', Tabs)
}
}
Use it in your Markdown:
<Tabs :tabs="[{ title: 'npm' }, { title: 'yarn' }, { title: 'pnpm' }]">
<template #tab-0>
```bash
npm install vitepress
```
</template>
<template #tab-1>
```bash
yarn add vitepress
```
</template>
<template #tab-2>
```bash
pnpm add vitepress
```
</template>
</Tabs>
Advanced Container Techniques
Containers with Icons
Add icons to your containers:
// .vitepress/config.js
export default {
markdown: {
config: (md) => {
const { container } = require('markdown-it-container')
md.use(container, 'icon-info', {
render: (tokens, idx) => {
const token = tokens[idx]
if (token.nesting === 1) {
return `<div class="custom-container info icon-container">\n<p class="custom-container-title"><svg class="icon" viewBox="0 0 24 24"><path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10s10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"></path></svg> INFO</p>\n`
} else {
return '</div>\n'
}
}
})
}
}
}
Add the CSS:
/* .vitepress/theme/custom.css */
.icon-container .custom-container-title {
display: flex;
align-items: center;
}
.icon-container .icon {
width: 20px;
height: 20px;
margin-right: 8px;
}
Containers with Custom Components
Create containers that include Vue components:
// .vitepress/config.js
export default {
markdown: {
config: (md) => {
const { container } = require('markdown-it-container')
md.use(container, 'demo', {
render: (tokens, idx) => {
const token = tokens[idx]
if (token.nesting === 1) {
return `<DemoContainer>\n`
} else {
return '</DemoContainer>\n'
}
}
})
}
}
}
Create the component:
<!-- .vitepress/theme/components/DemoContainer.vue -->
<script setup>
import { ref } from 'vue'
const showCode = ref(false)
</script>
<template>
<div class="demo-container">
<div class="demo-content">
<slot></slot>
</div>
<div class="demo-footer">
<button @click="showCode = !showCode">
{{ showCode ? 'Hide Code' : 'Show Code' }}
</button>
</div>
<div v-if="showCode" class="demo-code">
<slot name="code"></slot>
</div>
</div>
</template>
Register it in your theme:
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme'
import DemoContainer from './components/DemoContainer.vue'
export default {
...DefaultTheme,
enhanceApp({ app }) {
app.component('DemoContainer', DemoContainer)
}
}
Best Practices
Be Consistent: Use containers consistently throughout your documentation.
Don't Overuse: Too many containers can make your documentation look cluttered.
Choose Appropriate Types: Use the right container type for the content (info for general information, warning for potential issues, etc.).
Keep Content Focused: Each container should focus on a single concept or piece of information.
Use Clear Titles: Make container titles descriptive and concise.
Consider Accessibility: Ensure your custom containers maintain good contrast and are accessible to screen readers.
Test in Both Themes: Verify that your containers look good in both light and dark modes.
Frequently Asked Questions
How can I create a container with a custom background image?
You can use CSS to add background images to containers:
.custom-container.special {
background-image: url('/background.png');
background-size: cover;
background-position: center;
color: white;
text-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
}
Can I use containers in the sidebar or navigation?
Containers are primarily designed for use in the main content area. For sidebar or navigation customization, you should use custom components or CSS.
How do I make containers responsive?
Use CSS media queries to adjust container styling on different screen sizes:
.custom-container {
padding: 1rem;
}
@media (max-width: 640px) {
.custom-container {
padding: 0.5rem;
}
}
Related Resources
This document will be continuously updated. If you have any questions, please provide feedback through GitHub Issues.