Skip to content

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

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

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

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

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

md
::: note
This is a custom note container.
:::

Advanced Custom Container

Create more advanced containers with custom rendering logic:

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

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

css
/* .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:

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

css
/* .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:

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

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

vue
<!-- .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:

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

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

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

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:

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

vue
<!-- .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:

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

  1. Be Consistent: Use containers consistently throughout your documentation.

  2. Don't Overuse: Too many containers can make your documentation look cluttered.

  3. Choose Appropriate Types: Use the right container type for the content (info for general information, warning for potential issues, etc.).

  4. Keep Content Focused: Each container should focus on a single concept or piece of information.

  5. Use Clear Titles: Make container titles descriptive and concise.

  6. Consider Accessibility: Ensure your custom containers maintain good contrast and are accessible to screen readers.

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

css
.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:

css
.custom-container {
  padding: 1rem;
}

@media (max-width: 640px) {
  .custom-container {
    padding: 0.5rem;
  }
}

This document will be continuously updated. If you have any questions, please provide feedback through GitHub Issues.

VitePress Development Guide