Skip to content

feat(customization): sidenav components / skip title update #4630

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
7 changes: 7 additions & 0 deletions docs/en/reference/default-theme-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,13 @@ Can be used to customize the aria-label of the language toggle button in navbar.

Can be used to customize the label of the skip to content link. This link is shown when the user is navigating the site using a keyboard.

## skipTitleUpdate

- Type: `boolean`
- Default: `false`

Can be used to skip the default title update logic so it can be implemented on your own.

## externalLinkIcon

- Type: `boolean`
Expand Down
21 changes: 21 additions & 0 deletions docs/en/reference/default-theme-nav.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,27 @@ export default {

Refer [`socialLinks`](./default-theme-config#sociallinks).

## Custom Text Item Component

If you want to use the default theme but modify only the text of the items, you can override the `SidebarTextItem` inside the `enhanceApp` function. For example:

```js
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme';
import CustomSidebarTextItem from './components/CustomSidebarTextItem.vue';

export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.component('SidebarTextItem', CustomSidebarTextItem);
}
};
```

### Creating a Custom `SidebarTextItem`

To create your own `SidebarTextItem`, define a Vue component that accepts a `content` prop and renders it inside a chosen HTML tag. This allows you to fully customize how the text appears while maintaining compatibility with the default theme.

## Custom Components

You can include custom components in the navigation bar by using the `component` option. The `component` key should be the Vue component name, and must be registered globally using [Theme.enhanceApp](../guide/custom-theme#theme-interface).
Expand Down
21 changes: 21 additions & 0 deletions docs/en/reference/default-theme-search.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,27 @@ export default defineConfig({
})
```

### Custom Local Search Box Item Component

If you want to customize the search result items in the local search page, you can override the `LocalSearchBoxItem` component similarly:

```js
// .vitepress/theme/index.js
import DefaultTheme from 'vitepress/theme';
import CustomLocalSearchBoxItem from './components/CustomLocalSearchBoxItem.vue';

export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.component('LocalSearchBoxItem', CustomLocalSearchBoxItem);
}
};
```

#### Creating a Custom `LocalSearchBoxItem`

To create your own `LocalSearchBoxItem`, define a Vue component that accepts a `content` prop and renders it inside a chosen HTML tag, such as a `<span>`. This allows you to control how search results are displayed while keeping the default functionality.

## Algolia Search

VitePress supports searching your docs site using [Algolia DocSearch](https://docsearch.algolia.com/docs/what-is-docsearch). Refer their getting started guide. In your `.vitepress/config.ts` you'll need to provide at least the following to make it work:
Expand Down
13 changes: 13 additions & 0 deletions src/client/app/components/LocalSearchBoxItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { defineComponent, h } from 'vue'

export const LocalSearchBoxItem = defineComponent({
props: {
content: {
type: String,
required: true
}
},
render() {
return h('span', { innerHTML: this.content })
}
})
17 changes: 17 additions & 0 deletions src/client/app/components/SidebarTextItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { defineComponent, h } from 'vue'

export const SidebarTextItem = defineComponent({
props: {
content: {
type: String,
required: true
},
is: {
type: [Object, String],
default: 'p'
}
},
render() {
return h(this.is, { innerHTML: this.content })
}
})
8 changes: 5 additions & 3 deletions src/client/app/composables/head.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,11 @@ export function useUpdateHead(route: Route, siteDataByRouteRef: Ref<SiteData>) {
const frontmatterHead = (pageData && pageData.frontmatter.head) || []

// update title and description
const title = createTitle(siteData, pageData)
if (title !== document.title) {
document.title = title
if (!siteData.themeConfig.skipTitleUpdate) {
const title = createTitle(siteData, pageData)
if (title !== document.title) {
document.title = title
}
}

const description = pageDescription || siteData.description
Expand Down
4 changes: 4 additions & 0 deletions src/client/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { usePrefetch } from './composables/preFetch'
import { dataSymbol, initData, siteDataRef, useData } from './data'
import { RouterSymbol, createRouter, scrollTo, type Router } from './router'
import { inBrowser, pathToFile } from './utils'
import { SidebarTextItem } from './components/SidebarTextItem'
import { LocalSearchBoxItem } from './components/LocalSearchBoxItem'

function resolveThemeExtends(theme: typeof RawTheme): typeof RawTheme {
if (theme.extends) {
Expand Down Expand Up @@ -78,6 +80,8 @@ export async function createApp() {
// install global components
app.component('Content', Content)
app.component('ClientOnly', ClientOnly)
app.component('SidebarTextItem', SidebarTextItem)
app.component('LocalSearchBoxItem', LocalSearchBoxItem)

// expose $frontmatter & $params
Object.defineProperties(app.config.globalProperties, {
Expand Down
4 changes: 2 additions & 2 deletions src/client/theme-default/components/VPLocalSearchBox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -526,11 +526,11 @@ function onMouseMove(e: MouseEvent) {
:key="index"
class="title"
>
<span class="text" v-html="t" />
<LocalSearchBoxItem class="text" :content="t" />
<span class="vpi-chevron-right local-search-icon" />
</span>
<span class="title main">
<span class="text" v-html="p.title" />
<LocalSearchBoxItem class="text" :content="p.title" />
</span>
</div>

Expand Down
4 changes: 2 additions & 2 deletions src/client/theme-default/components/VPSidebarItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ function onCaretClick() {
:rel="item.rel"
:target="item.target"
>
<component :is="textTag" class="text" v-html="item.text" />
<SidebarTextItem :is="textTag" class="text" :content="item.text" />
</VPLink>
<component v-else :is="textTag" class="text" v-html="item.text" />
<SidebarTextItem v-else :is="textTag" class="text" :content="item.text" />

<div
v-if="item.collapsed != null && item.items && item.items.length"
Expand Down
5 changes: 5 additions & 0 deletions types/default-theme.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ export namespace DefaultTheme {
*/
skipToContentLabel?: string

/**
* @default false
*/
skipTitleUpdate?: boolean

search?:
| { provider: 'local'; options?: LocalSearchOptions }
| { provider: 'algolia'; options: AlgoliaSearchOptions }
Expand Down