File Based Routing
File based routing is a way to automate routing instead of statically typing routes. Check how to enable file based routing using Unplugin Vue Router.
Additionally, let's enable static Markdown content to be part of a page or the entire page.
Enable in project
- Install
unplugin-vue-router
andunplugin-vue-markdown
ps
npm install -D unplugin-vue-router
npm install unplugin-vue-markdown
- Add the plugin to
./vite.config.ts
:
ts
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
import VueRouter from 'unplugin-vue-router/vite'
import Markdown from 'unplugin-vue-markdown/vite'
// https://vite.dev/config/
export default defineConfig({
plugins: [
Markdown({}),
VueRouter({ extensions: ['.vue', '.md'] }),
vue({
include: [/\.vue$/, /\.md$/]
}),
vueDevTools(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
})
Markdown style can be adjusted by modifying main css:
@/assets/main.css
css
div.markdown-body h1 {
margin-bottom: 24px;
}
div.markdown-body h2 {
margin-top: 8px;
margin-bottom: 16px;
}
div.markdown-body h3 {
margin-top: 8px;
margin-bottom: 12px;
}
div.markdown-body ul {
margin-bottom: 8px;
}
div.markdown-body li {
margin-left: 24px;
}
div.markdown-body p {
margin-bottom: 8px;
}
div.markdown-body hr {
margin-top: 8px;
margin-bottom: 8px;
}
- Add auto-generated route file and compiler option to
./tsconfig.app.json
json
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue", "wiki/**/*", "./typed-router.d.ts"],
"exclude": ["src/**/__tests__/*"],
//
}
- Add the
unplugin-vue-router/client
types to./env.d.ts
file.
ts
/// <reference types="vite/client" />
/// <reference types="unplugin-vue-router/client" />
- Exclude
vue-router/auto
from VSCode import suggestions by adding this setting to your./.vscode/settings.json
:
json
{
//
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.preferences.autoImportFileExcludePatterns": ["vue-router/auto$"]
//
}
- Allow pages to be single worded in
./.eslintrc.cjs
js
//
export default [
// ...
{
files: ['src/pages/**/*.vue'],
rules: {
'vue/multi-word-component-names': 'off',
},
},
]
- Rename
@/views
folder to@/pages
@/pages/HomeView.vue
to@/pages/index.vue
@/pages/AboutView.vue
to@/pages/about.md
- Replace contents of
@/pages/about.md
with Markdown
md
---
title: About Our Platform
description: Learn more about our platform and its capabilities
icon: $mdiInformation
role: public
color: #FA8531
---
# About
This is an `About` page
- Modify
@/router/index.ts
ts
import { createRouter, createWebHistory } from 'vue-router'
import { routes, handleHotUpdate } from 'vue-router/auto-routes'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes,
})
if (import.meta.hot) {
handleHotUpdate(router)
}
export default router
- Test the app
npm run dev
that the new file based routing works as expected.
- Additionally, add feature to
vite.config.ts
to extend route with meta information from.md
files.
ts
import { fileURLToPath, URL } from 'node:url'
import { promises as fs } from 'node:fs'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
import VueRouter from 'unplugin-vue-router/vite'
import Markdown from 'unplugin-vue-markdown/vite'
async function extractMetaFromMarkdown(absolutePath: string): Promise<Record<string, unknown> | null> {
try {
const mdContent = await fs.readFile(absolutePath, 'utf-8');
const metaRegex = /^---\n([\s\S]*?)\n---/;
const match = mdContent.match(metaRegex);
if (match && match[1]) {
const metaString = match[1];
const metaLines = metaString.split("\n");
const metaObject: Record<string, unknown> = {};
for (const line of metaLines) {
const [key, ...valueParts] = line.split(":");
if (key && valueParts.length) {
const value = valueParts.join(":").trim();
metaObject[key.trim()] = value.startsWith('"') && value.endsWith('"')
? value.slice(1, -1)
: value;
}
}
return metaObject;
}
return null;
} catch (error) {
console.error(`Error reading file at ${absolutePath}:`, error);
return null;
}
}
// https://vite.dev/config/
export default defineConfig({
plugins: [
Markdown({}),
VueRouter({ extensions: ['.vue', '.md'], async extendRoute(route) {
if (route.component?.endsWith('.md')) {
const meta = await extractMetaFromMarkdown(route.component)
if (meta) route.meta = { ...route.meta, ...meta }
}
} }),
vue({
include: [/\.vue$/, /\.md$/]
}),
vueDevTools(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
})