skip to content

VitePress

VitePress turns Markdown into a fast documentation site, with Vue components usable inside the content and a themeable default layout. It comes up whenever owning the docs site is part of the job.

on this pageshow

questions

6

What does VitePress 1.6 do with a folder of Markdown files, and what does a minimal project need to build a documentation site?

level: juniorimportance: must knowfreq 30%

answer

  1. Markdown becomes Vue components
  2. static HTML first, then a SPA
  3. a special dot folder
  4. file path equals URL
  5. dev, build, preview

basics

~20 s

VitePress compiles each Markdown file into a Vue component, prerenders every page to static HTML at build time, then hydrates it into a single-page app. A minimal project is Markdown pages plus .vitepress/config, run with vitepress dev and build.

solid answer

~40 s

VitePress is a static site generator built on Vite and Vue for documentation-style sites. Each `.md` file is compiled into a Vue single-file component; `vitepress build` renders every page to HTML in Node, so the first visit gets plain HTML, and the page then hydrates into a Vue app that handles later navigation without full reloads. A minimal project is a folder such as `docs/` with an `index.md` and a `.vitepress/config.mts` that default-exports `defineConfig({ title, description, themeConfig })`. Routes follow file paths: `guide/installation.md` becomes `/guide/installation.html`. You run `vitepress dev docs` while writing, `vitepress build docs` to produce `docs/.vitepress/dist`, and `vitepress preview docs` to serve that output locally. The default theme supplies the nav bar, sidebar, local search and dark mode, configured through `themeConfig`.

code

bash · 4 lines
bash
npm add -D vitepress
npx vitepress dev docs      # write with hot updates
npx vitepress build docs    # prerender to docs/.vitepress/dist
npx vitepress preview docs  # serve the build on localhost:4173

go deeper

for a junior

Recall that VitePress turns Markdown into a prerendered static site, that config lives in .vitepress/config, and the dev, build and preview commands.

for a middle

Explain Markdown-to-Vue compilation, prerender then hydrate, file-based routing and where the build output goes.

for a senior

Show when VitePress fits a docs site and what the static model rules out, such as request-time logic and browser-only code during the build.

for a principal

Weigh owning a VitePress docs site in the library repo against other publishing setups: release coupling, contributor workflow and hosting constraints.

## What VitePress is for VitePress is a **static site generator** built on Vite and Vue, aimed at content-heavy sites such as the documentation of a Vue component library. You write Markdown; VitePress applies a theme and produces static HTML files that any static file host can serve. What sets it apart from a plain Markdown-to-HTML tool is how the page works in the browser: 1. **Each Markdown page becomes a Vue component.** VitePress compiles the Markdown to HTML and then treats the result as a Vue single-file component, so pages can use Vue syntax and components. 2. **The build prerenders every page.** `vitepress build` renders each page to HTML in Node, so the first visit is fast and indexable. 3. **The page then hydrates into a single-page app.** Later navigation fetches the next page's chunk and swaps content without a full reload, and links in the viewport are prefetched. 4. **Static parts are cheap.** Vue's compiler separates static from dynamic content, so the purely static parts of a page are left out of the initial JavaScript payload and skipped during hydration. ## A minimal project For a UI library repository, the docs usually live in a `docs/` folder: | Path | Purpose | |---|---| | `docs/index.md` | the home page, served at `/` | | `docs/guide/installation.md` | served at `/guide/installation.html` | | `docs/.vitepress/config.mts` | site config: title, description, theme config | | `docs/.vitepress/theme/` | optional theme customisation | | `docs/public/` | files copied as-is, such as a logo | The `.vitepress` directory is reserved: it holds the config, the dev cache (`.vitepress/cache`), the default build output (`.vitepress/dist`) and any theme code. The config file may be `config.js`, `config.ts`, `config.mjs` or `config.mts`. ```ts // docs/.vitepress/config.mts import { defineConfig } from 'vitepress' export default defineConfig({ title: 'Nimbus UI', description: 'Accessible Vue 3 components', themeConfig: { nav: [{ text: 'Guide', link: '/guide/installation' }], sidebar: [{ text: 'Components', items: [{ text: 'Button', link: '/components/button' }] }], search: { provider: 'local' }, }, }) ``` ## The three commands - `vitepress dev docs` starts the Vite dev server with hot updates. It renders pages in the browser only. - `vitepress build docs` prerenders every page and writes `docs/.vitepress/dist`. - `vitepress preview docs` serves that output locally, on port 4173 by default, so you can test the real build. `vitepress init` scaffolds the folder and config through a few prompts. ## File-based routing The URL mirrors the file path relative to the source directory, which is the project root unless `srcDir` says otherwise: - `index.md` becomes `/index.html`, reachable as `/`; - `guide/index.md` becomes `/guide/index.html`, reachable as `/guide/`; - `components/button.md` becomes `/components/button.html`. By default links and files keep the `.html` extension; the `cleanUrls` option removes it from links when the host can serve extensionless URLs. ## What the default theme adds Without writing any Vue, the default theme gives you a nav bar, a sidebar, an on-page outline, previous and next links, a dark-mode toggle (`appearance` is on by default) and, with `search: { provider: 'local' }`, in-browser full-text search. All of it is driven by `themeConfig` in the site config and by per-page frontmatter. ## Where it stops VitePress builds a static site: there is no server at request time. Anything dynamic runs in the browser after hydration, and every page and component must render in Node during the build. ## Why a component library picks it For the docs of an open-source Vue UI library, VitePress fits for concrete reasons: - **Demos are real Vue.** A page can import the library's components and render them live, with no separate playground build. - **One toolchain.** The docs run on Vite, like the library itself, so aliases, TypeScript and single-file components behave the same way. - **Docs next to code.** The `docs/` folder sits in the library repository, so a pull request can change a component and its page together. - **Static output.** The result is a folder of files that any static host can serve, with no server to operate. What it does not give you is request-time behaviour. The built-in local search runs in the browser against an index made at build time, and anything personalised happens client-side after hydration. ## Minimal workflow from zero 1. Install `vitepress` as a dev dependency. 2. Run `npx vitepress init` and choose `./docs` as the root. 3. Write `docs/index.md` and a first guide page. 4. Run `vitepress dev docs` while writing, and `vitepress build docs` in CI.

  • Why is a VitePress site fast on first load even though every page is a Vue component?
    The build prerenders each page to HTML, so the first response is ready to display. Vue's compiler also separates static Markdown from dynamic parts; static content is left out of the initial JavaScript payload and skipped during hydration, so a mostly-text page ships little script.
  • What is the difference between the project root and srcDir in VitePress?
    The project root is where VitePress looks for the `.vitepress` folder, set by the directory you pass to the CLI. `srcDir` is where the Markdown pages live and defaults to the project root. Setting `srcDir: './src'` moves the pages without moving `.vitepress`, and URLs are then computed relative to `srcDir`.

saying these in an interview costs you the question

  • VitePress renders every page on a server at request time.
  • Each page navigation in a VitePress site is a full page reload.
  • The VitePress config file lives at the project root as vitepress.config.ts.
  • The default build output goes to a dist folder next to package.json.
  • Using Vue components in pages requires a separate plugin.
open as a page

In VitePress 1.6, how do you render a live Vue component demo inside a Markdown page, and which Markdown-specific rules can break it?

level: middleimportance: must knowfreq 28%

basics

~20 s

Add a <script setup> block after the frontmatter, import the demo component and use its tag in the Markdown. Keep component names PascalCase or hyphenated, remember code fences are v-pre, and prefer <style module> to <style scoped>.

open as a page

In VitePress 1.6, how would you write a component page that shows its real source file, tabbed install commands and a warning box without copying code by hand?

level: middleimportance: should knowfreq 20%

basics

~20 s

Import the demo file as a code block with <<< @/demos/ButtonBasic.vue, group the npm, pnpm and yarn fences in a ::: code-group container, and add a ::: warning container. Frontmatter such as outline and editLink tunes the page.

open as a page

A VitePress 1.6 date-picker demo works in `vitepress dev`, but `vitepress build` fails with `window is not defined`, even inside `<ClientOnly>`. Why, and how do you fix it?

level: seniorimportance: should knowfreq 25%

basics

~20 s

vitepress dev renders only in the browser, but build prerenders in Node. <ClientOnly> delays rendering until mount, yet the library still touches window when it is imported. Load it after mount with defineClientComponent or a dynamic import.

open as a page

How do you extend the VitePress 1.6 default theme to register demo components globally, apply a brand colour and add a release banner to every page, without forking it?

level: seniorimportance: should knowfreq 18%

basics

~10 s

Create .vitepress/theme/index.ts that exports { extends: DefaultTheme, enhanceApp, Layout }: register components in enhanceApp, override --vp-c-brand-1 in an imported CSS file, and fill the default Layout's layout-top slot through a wrapper.

open as a page