Skip to content

Instantly share code, notes, and snippets.

@adiroiban
Created July 24, 2025 16:36
Show Gist options
  • Save adiroiban/5705257e9f7acee0eba066a4357d0d43 to your computer and use it in GitHub Desktop.
Save adiroiban/5705257e9f7acee0eba066a4357d0d43 to your computer and use it in GitHub Desktop.
VuePress blog pagination
<script setup lang="ts">
import {useData, withBase} from 'vitepress'
import Footer from './components/Footer.vue'
import Header from './components/Header.vue'
const { params, frontmatter } = useData()
</script>
<template>
<Header />
<div class="text-center">
<h2 class="text-3xl font-bold tracking-tight text-(--color-text-title) sm:text-4xl">
{{ frontmatter.title }}
</h2>
<p class="mx-auto mt-3 max-w-2xl text-xl text-(--color-text-main) sm:mt-4">
{{ frontmatter.subtitle }}
</p>
</div>
<article v-for="page in params.content" class="flex flex-col overflow-hidden rounded-lg shadow-lg">
{{ page.frontmatter.title }}
</article>
<div class="mt-12 flex">
<div v-show="params.prevURL" class="mt-1">
<a :href="params.prevURL">Previous page</a>
</div>
<div v-show="params.nextURL" class="ml-auto text-right">
<a :href="params.nextURL">Next page</a>
</div>
</div>
<Footer />
</template>
title subtitle category layout
Random rambling
General non-technical pages
blog
pages-index
import { getIndexPages } from '../../.vitepress/theme/utils'
// My use case is more complicates as I have both /blog/*.md and /example/*.md
export default getIndexPages(__dirname, '/blog', 10)
import fs from 'fs'
import matter from 'gray-matter'
export type PageMetadata = {
path: string
url: string
frontmatter: any
}
export type IndexPage = {
content: PageMetadata[]
allPages: PageMetadata[]
prevURL: string
nextURL: string
pageNumber: number
}
/**
* Filter files that are not markdown content.
*/
function filterPage(path: string): boolean {
// Online include markdownd files and explude path templates.
return path.endsWith('.md') && !path.endsWith('].md')
}
function getFileMetadata(filePath: string, basePath: string, prefix: string): PageMetadata {
const result = {path: filePath, url: '', frontmatter: {}}
result.url = filePath.replace('.md', '').replace('\\', '/')
result.url = `${prefix}/${result.url}.html`
const src = fs.readFileSync(basePath + '/' + filePath, 'utf-8')
result.frontmatter = matter(src).data
return result
}
/**
* Helper to be used in `index[counter].paths.ts` files.
*/
export function getIndexPages(srcPath: string, prefix: string, page_size: number = 10) {
return {
paths: () => {
const allPages = fs
.readdirSync(srcPath, { recursive: true })
.filter((file) => filterPage(file))
.map((file) => getFileMetadata(file, srcPath, prefix))
.sort((a, b) => b.frontmatter.date > a.frontmatter.date ? 1 : -1)
const total = Math.ceil(allPages.length / page_size)
return Array.from({length: total}).map((_, index) => {
const current = index + 1
let prevURL = `index-${current - 1}.html`
if (current === 1) {
prevURL = ''
}
if (current === 2) {
prevURL = 'index.html'
}
const nextURL =
current < total ? `index-${current + 1}.html` : ''
const cursor = current === 1 ? '' : `-${current}`
const content = allPages.slice(index * page_size, (index + 1) * page_size)
let indexPageContent: IndexPage = {
content,
allPages,
prevURL,
nextURL,
pageNumber: current,
}
// cursor is used by vitepress to generate the path.
// The other values are custom content.
return {params: {cursor, ...indexPageContent}}
})
},
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment