diff --git a/docs/en/guide/custom-theme.md b/docs/en/guide/custom-theme.md index 957c27dc..21cf4e26 100644 --- a/docs/en/guide/custom-theme.md +++ b/docs/en/guide/custom-theme.md @@ -38,7 +38,12 @@ interface Theme { */ enhanceApp?: (ctx: EnhanceAppContext) => Awaitable /** - * Extend another theme, calling its `enhanceApp` before ours + * Runs inside the root component's `setup()` + * @optional + */ + setup?: () => void + /** + * Extend another theme, calling its `enhanceApp` and `setup` before ours * @optional */ extends?: Theme @@ -88,6 +93,26 @@ export default { Return `false` from `onBeforeRouteChange` or `onBeforePageLoad` to cancel navigation. +The `setup` hook runs inside the root component's `setup()`, so Composition API calls (`onMounted`, `watch`, composables, ...) work there without wrapping the layout component: + +```ts [.vitepress/theme/index.ts] +import { watch } from 'vue' +import { useData } from 'vitepress' +import DefaultTheme from 'vitepress/theme' + +export default { + extends: DefaultTheme, + setup() { + const { page } = useData() + watch(() => page.value.relativePath, (path) => { + console.log('now viewing', path) + }) + } +} +``` + +With `extends`, each theme's `setup` runs base-first, like `enhanceApp`. It also runs during SSR/SSG rendering, so keep browser-only work inside `onMounted`. + The default export is the only contract for a custom theme, and only the `Layout` property is required. So technically, a VitePress theme can be as simple as a single Vue component. Inside your layout component, it works just like a normal Vite + Vue 3 application. Do note the theme also needs to be [SSR-compatible](./ssr-compat). diff --git a/src/client/app/index.ts b/src/client/app/index.ts index 15f489e3..952e52f5 100644 --- a/src/client/app/index.ts +++ b/src/client/app/index.ts @@ -26,8 +26,12 @@ function resolveThemeExtends(theme: typeof RawTheme): typeof RawTheme { ...base, ...theme, async enhanceApp(ctx) { - if (base.enhanceApp) await base.enhanceApp(ctx) - if (theme.enhanceApp) await theme.enhanceApp(ctx) + await base.enhanceApp?.(ctx) + await theme.enhanceApp?.(ctx) + }, + setup() { + base.setup?.() + theme.setup?.() } } } diff --git a/src/client/app/theme.ts b/src/client/app/theme.ts index 8c3fd2c1..4ebb222a 100644 --- a/src/client/app/theme.ts +++ b/src/client/app/theme.ts @@ -15,7 +15,8 @@ export interface Theme { extends?: Theme /** - * @deprecated can be replaced by wrapping layout component + * Runs inside the root component's `setup()` (during SSR too). With + * `extends`, setups run base-first, like `enhanceApp`. */ setup?: () => void