0
# Client-Side API
1
2
VitePress client-side API provides composables and utilities for accessing runtime data, navigation, and page information. Essential for building custom themes and Vue components in markdown files.
3
4
## Capabilities
5
6
### Core Composables
7
8
Vue composables for accessing VitePress runtime state and functionality.
9
10
#### useData
11
12
Access VitePress runtime data including site configuration, page data, and theme configuration.
13
14
```typescript { .api }
15
/**
16
* Access VitePress runtime data
17
* @returns VitePressData object containing reactive site and page data
18
*/
19
function useData<T = any>(): VitePressData<T>;
20
21
interface VitePressData<T> {
22
/**
23
* Reactive site-level data and configuration
24
*/
25
site: Ref<SiteData<T>>;
26
27
/**
28
* Reactive theme configuration
29
*/
30
theme: Ref<T>;
31
32
/**
33
* Reactive current page data
34
*/
35
page: Ref<PageData>;
36
37
/**
38
* Reactive frontmatter of current page
39
*/
40
frontmatter: Ref<Record<string, any>>;
41
42
/**
43
* Reactive route parameters for dynamic routes
44
*/
45
params: Ref<Record<string, string>>;
46
47
/**
48
* Computed page title (from frontmatter or page data)
49
*/
50
title: Ref<string>;
51
52
/**
53
* Computed page description (from frontmatter or page data)
54
*/
55
description: Ref<string>;
56
57
/**
58
* Current language code
59
*/
60
lang: Ref<string>;
61
62
/**
63
* Text direction (ltr/rtl)
64
*/
65
dir: Ref<string>;
66
67
/**
68
* Current locale index for multi-language sites
69
*/
70
localeIndex: Ref<string>;
71
72
/**
73
* Dark mode state (reactive)
74
*/
75
isDark: Ref<boolean>;
76
}
77
```
78
79
**Usage Examples:**
80
81
```vue
82
<script setup>
83
import { useData } from "vitepress";
84
85
const { site, page, theme, frontmatter, isDark } = useData();
86
87
// Access site information
88
console.log("Site title:", site.value.title);
89
console.log("Base URL:", site.value.base);
90
91
// Access page information
92
console.log("Page title:", page.value.title);
93
console.log("Page path:", page.value.relativePath);
94
95
// Access theme configuration
96
console.log("Nav items:", theme.value.nav);
97
98
// Access frontmatter
99
console.log("Page tags:", frontmatter.value.tags);
100
101
// React to dark mode changes
102
watch(isDark, (dark) => {
103
console.log("Dark mode:", dark ? "enabled" : "disabled");
104
});
105
</script>
106
107
<template>
108
<div :class="{ dark: isDark }">
109
<h1>{{ page.title }}</h1>
110
<p>{{ page.description }}</p>
111
<div v-if="frontmatter.tags">
112
<span v-for="tag in frontmatter.tags" :key="tag" class="tag">
113
{{ tag }}
114
</span>
115
</div>
116
</div>
117
</template>
118
```
119
120
#### useRouter
121
122
Access VitePress router for programmatic navigation and route handling.
123
124
```typescript { .api }
125
/**
126
* Access VitePress router instance
127
* @returns Router object with navigation methods and hooks
128
*/
129
function useRouter(): Router;
130
131
interface Router {
132
/**
133
* Current route information
134
*/
135
route: Route;
136
137
/**
138
* Navigate to a new URL
139
* @param to - Target URL or path
140
* @returns Promise that resolves after navigation
141
*/
142
go(to?: string): Promise<void>;
143
144
/**
145
* Hook called before route changes
146
*/
147
onBeforeRouteChange?: (to: string) => Awaitable<void | boolean>;
148
149
/**
150
* Hook called before page loads
151
*/
152
onBeforePageLoad?: (to: string) => Awaitable<void | boolean>;
153
154
/**
155
* Hook called after page loads
156
*/
157
onAfterPageLoad?: (to: string) => Awaitable<void>;
158
159
/**
160
* Hook called after route changes
161
*/
162
onAfterRouteChange?: (to: string) => Awaitable<void>;
163
}
164
```
165
166
**Usage Examples:**
167
168
```vue
169
<script setup>
170
import { useRouter, onMounted } from "vitepress";
171
172
const router = useRouter();
173
174
// Navigate programmatically
175
const goToPage = (path) => {
176
router.go(path);
177
};
178
179
// Set up navigation hooks
180
onMounted(() => {
181
router.onBeforeRouteChange = async (to) => {
182
console.log("Navigating to:", to);
183
// Return false to cancel navigation
184
if (to.includes("restricted")) {
185
return false;
186
}
187
};
188
189
router.onAfterPageLoad = async (to) => {
190
console.log("Page loaded:", to);
191
// Update analytics, scroll position, etc.
192
trackPageView(to);
193
};
194
});
195
196
const trackPageView = (path) => {
197
// Analytics tracking logic
198
};
199
</script>
200
201
<template>
202
<nav>
203
<button @click="goToPage('/guide/')">Guide</button>
204
<button @click="goToPage('/api/')">API</button>
205
<button @click="goToPage('/examples/')">Examples</button>
206
</nav>
207
</template>
208
```
209
210
#### useRoute
211
212
Access current route information and reactive route state.
213
214
```typescript { .api }
215
/**
216
* Access current route information
217
* @returns Current Route object with path, data, and component
218
*/
219
function useRoute(): Route;
220
221
interface Route {
222
/**
223
* Current path
224
*/
225
path: string;
226
227
/**
228
* Current page data
229
*/
230
data: PageData;
231
232
/**
233
* Current page component
234
*/
235
component: Component | null;
236
}
237
```
238
239
**Usage Examples:**
240
241
```vue
242
<script setup>
243
import { useRoute, computed } from "vitepress";
244
245
const route = useRoute();
246
247
// Reactive computed properties based on route
248
const isHomePage = computed(() => route.path === "/");
249
const isApiPage = computed(() => route.path.startsWith("/api/"));
250
const currentSection = computed(() => {
251
const segments = route.path.split("/").filter(Boolean);
252
return segments[0] || "home";
253
});
254
255
// Access route data
256
console.log("Current path:", route.path);
257
console.log("Page headers:", route.data.headers);
258
</script>
259
260
<template>
261
<div>
262
<div v-if="isHomePage" class="home-banner">
263
Welcome to the homepage!
264
</div>
265
266
<nav class="breadcrumb">
267
<span class="section" :class="{ active: currentSection === 'home' }">
268
Home
269
</span>
270
<span v-if="!isHomePage" class="section active">
271
{{ currentSection }}
272
</span>
273
</nav>
274
275
<div v-if="isApiPage" class="api-notice">
276
You're viewing API documentation
277
</div>
278
</div>
279
</template>
280
```
281
282
### Client Utilities
283
284
Utility functions for common client-side operations and URL handling.
285
286
#### withBase
287
288
Prepend configured base URL to internal links and paths.
289
290
```typescript { .api }
291
/**
292
* Prepend configured base to internal URLs
293
* @param path - URL path string
294
* @returns URL with base prepended
295
*/
296
function withBase(path: string): string;
297
```
298
299
**Usage Examples:**
300
301
```vue
302
<script setup>
303
import { withBase, useData } from "vitepress";
304
305
const { site } = useData();
306
307
// Ensure links work with custom base URLs
308
const logoUrl = withBase("/logo.svg");
309
const apiUrl = withBase("/api/reference");
310
311
// Dynamic asset paths
312
const getAssetUrl = (filename) => withBase(`/assets/${filename}`);
313
</script>
314
315
<template>
316
<header>
317
<img :src="logoUrl" :alt="site.title" />
318
<nav>
319
<a :href="withBase('/guide/')">Guide</a>
320
<a :href="apiUrl">API Reference</a>
321
</nav>
322
</header>
323
324
<main>
325
<img :src="getAssetUrl('hero-image.jpg')" alt="Hero" />
326
</main>
327
</template>
328
```
329
330
#### onContentUpdated
331
332
Register callbacks for content updates and page changes.
333
334
```typescript { .api }
335
/**
336
* Register callback for content updates
337
* @param fn - Callback function to execute on content updates
338
* @returns void (auto-unregisters on component unmount)
339
*/
340
function onContentUpdated(fn: () => any): void;
341
```
342
343
**Usage Examples:**
344
345
```vue
346
<script setup>
347
import { onContentUpdated, nextTick } from "vitepress";
348
349
// Update third-party widgets when content changes
350
onContentUpdated(() => {
351
// Reinitialize syntax highlighters
352
if (window.Prism) {
353
window.Prism.highlightAll();
354
}
355
356
// Update table of contents
357
updateTableOfContents();
358
359
// Scroll to hash if present
360
nextTick(() => {
361
const hash = window.location.hash;
362
if (hash) {
363
const element = document.querySelector(hash);
364
element?.scrollIntoView();
365
}
366
});
367
});
368
369
const updateTableOfContents = () => {
370
// Custom TOC update logic
371
const headers = document.querySelectorAll("h1, h2, h3, h4, h5, h6");
372
console.log("Found headers:", headers.length);
373
};
374
</script>
375
```
376
377
#### defineClientComponent
378
379
Define async components for client-side rendering with SSR compatibility.
380
381
```typescript { .api }
382
/**
383
* Define async component for client-side rendering
384
* @param loader - AsyncComponentLoader function
385
* @param args - Component arguments
386
* @param cb - Completion callback
387
* @returns Component definition object with SSR support
388
*/
389
function defineClientComponent(
390
loader: AsyncComponentLoader,
391
args?: any,
392
cb?: () => void
393
): Component;
394
395
type AsyncComponentLoader = () => Promise<Component>;
396
```
397
398
**Usage Examples:**
399
400
```vue
401
<script setup>
402
import { defineClientComponent } from "vitepress";
403
404
// Define client-only component (e.g., interactive widgets)
405
const InteractiveChart = defineClientComponent(
406
() => import("./components/InteractiveChart.vue"),
407
{
408
// Component props
409
data: chartData,
410
options: chartOptions
411
},
412
() => {
413
console.log("Chart component loaded");
414
}
415
);
416
417
// Define component with loading state
418
const HeavyWidget = defineClientComponent(
419
() => import("./components/HeavyWidget.vue"),
420
{},
421
() => {
422
// Initialize widget after loading
423
initializeWidget();
424
}
425
);
426
</script>
427
428
<template>
429
<div>
430
<h2>Data Visualization</h2>
431
<InteractiveChart v-if="chartData" />
432
<div v-else>Loading chart...</div>
433
434
<h2>Advanced Features</h2>
435
<HeavyWidget />
436
</div>
437
</template>
438
```
439
440
#### getScrollOffset
441
442
Calculate scroll offset based on site configuration for proper anchor positioning.
443
444
```typescript { .api }
445
/**
446
* Calculate scroll offset based on site configuration
447
* @returns Scroll offset in pixels
448
*/
449
function getScrollOffset(): number;
450
```
451
452
**Usage Examples:**
453
454
```vue
455
<script setup>
456
import { getScrollOffset } from "vitepress";
457
458
// Custom scroll behavior
459
const scrollToElement = (elementId) => {
460
const element = document.getElementById(elementId);
461
if (element) {
462
const offset = getScrollOffset();
463
const top = element.offsetTop - offset;
464
465
window.scrollTo({
466
top,
467
behavior: "smooth"
468
});
469
}
470
};
471
472
// Handle anchor clicks with proper offset
473
const handleAnchorClick = (event, hash) => {
474
event.preventDefault();
475
scrollToElement(hash.slice(1));
476
477
// Update URL without jumping
478
history.pushState(null, null, hash);
479
};
480
</script>
481
482
<template>
483
<nav class="table-of-contents">
484
<ul>
485
<li v-for="header in headers" :key="header.slug">
486
<a
487
:href="`#${header.slug}`"
488
@click="handleAnchorClick($event, `#${header.slug}`)"
489
>
490
{{ header.title }}
491
</a>
492
</li>
493
</ul>
494
</nav>
495
</template>
496
```
497
498
### Environment Detection
499
500
Constants and utilities for detecting the runtime environment.
501
502
#### inBrowser
503
504
Boolean constant indicating whether code is running in browser environment.
505
506
```typescript { .api }
507
/**
508
* Whether code is running in browser environment
509
*/
510
const inBrowser: boolean;
511
```
512
513
**Usage Examples:**
514
515
```vue
516
<script setup>
517
import { inBrowser } from "vitepress";
518
519
// Conditionally run browser-only code
520
if (inBrowser) {
521
// Safe to access window, document, localStorage, etc.
522
console.log("Running in browser");
523
console.log("User agent:", navigator.userAgent);
524
525
// Initialize browser-only features
526
initializeAnalytics();
527
setupKeyboardShortcuts();
528
} else {
529
console.log("Running on server (SSR)");
530
}
531
532
const initializeAnalytics = () => {
533
// Browser-only analytics code
534
if (typeof window !== "undefined" && window.gtag) {
535
window.gtag("config", "GA_MEASUREMENT_ID");
536
}
537
};
538
539
const setupKeyboardShortcuts = () => {
540
if (inBrowser) {
541
document.addEventListener("keydown", (event) => {
542
if (event.ctrlKey && event.key === "k") {
543
// Open search
544
openSearch();
545
}
546
});
547
}
548
};
549
</script>
550
```
551
552
### Content Component
553
554
Vue component for rendering markdown content within Vue templates.
555
556
#### Content
557
558
Component for rendering processed markdown content with Vue component support.
559
560
```typescript { .api }
561
/**
562
* Component for rendering markdown content
563
*/
564
const Content: Component;
565
```
566
567
**Usage Examples:**
568
569
```vue
570
<script setup>
571
import { Content, useData } from "vitepress";
572
573
const { page, frontmatter } = useData();
574
575
// Custom content wrapper with additional features
576
const showTableOfContents = computed(() =>
577
frontmatter.value.toc !== false && page.value.headers.length > 1
578
);
579
</script>
580
581
<template>
582
<div class="content-wrapper">
583
<header v-if="frontmatter.showHeader !== false">
584
<h1>{{ page.title }}</h1>
585
<p class="description">{{ page.description }}</p>
586
587
<div v-if="frontmatter.tags" class="tags">
588
<span v-for="tag in frontmatter.tags" :key="tag" class="tag">
589
{{ tag }}
590
</span>
591
</div>
592
</header>
593
594
<aside v-if="showTableOfContents" class="table-of-contents">
595
<h2>On This Page</h2>
596
<nav>
597
<ul>
598
<li v-for="header in page.headers" :key="header.slug">
599
<a :href="`#${header.slug}`">{{ header.title }}</a>
600
</li>
601
</ul>
602
</nav>
603
</aside>
604
605
<main class="content">
606
<!-- Render the actual markdown content -->
607
<Content />
608
</main>
609
610
<footer v-if="frontmatter.showFooter !== false">
611
<p v-if="page.lastUpdated">
612
Last updated: {{ new Date(page.lastUpdated).toLocaleDateString() }}
613
</p>
614
</footer>
615
</div>
616
</template>
617
618
<style scoped>
619
.content-wrapper {
620
display: grid;
621
grid-template-columns: 1fr 200px;
622
gap: 2rem;
623
}
624
625
.table-of-contents {
626
position: sticky;
627
top: 2rem;
628
height: fit-content;
629
}
630
</style>
631
```
632
633
### Advanced Client Utilities
634
635
Additional utilities for advanced client-side functionality.
636
637
#### Custom Data Loading
638
639
```typescript { .api }
640
/**
641
* Load custom data in client components
642
*/
643
interface DataLoader<T> {
644
/**
645
* Load data for current page
646
*/
647
load(): Promise<T>;
648
649
/**
650
* Cache loaded data
651
*/
652
cache?: boolean;
653
654
/**
655
* Invalidate cache conditions
656
*/
657
invalidate?: (route: Route) => boolean;
658
}
659
660
/**
661
* Create data loader for client components
662
*/
663
function createDataLoader<T>(loader: DataLoader<T>): () => Promise<T>;
664
```
665
666
#### Client-Side Search
667
668
```typescript { .api }
669
/**
670
* Client-side search functionality
671
*/
672
interface SearchProvider {
673
/**
674
* Search query
675
*/
676
search(query: string): Promise<SearchResult[]>;
677
678
/**
679
* Initialize search index
680
*/
681
initialize(): Promise<void>;
682
683
/**
684
* Update search index
685
*/
686
update(pages: PageData[]): Promise<void>;
687
}
688
689
interface SearchResult {
690
/**
691
* Page title
692
*/
693
title: string;
694
695
/**
696
* Page URL
697
*/
698
url: string;
699
700
/**
701
* Search excerpt
702
*/
703
excerpt: string;
704
705
/**
706
* Search relevance score
707
*/
708
score: number;
709
}
710
```