How to create category and tag pages on VuePress
VuePress itself doesn't have any pages for tags or categories. You can create them by using @vuepress/blog
plugin, but I decided to customize for this function because I want to use Layout.vue
as a layout file for tag and category pages and control details.
Note that the word taxonomy
is referred to as tag
and category
in this article.
# eject default theme
Although VuePress default theme is inside VuePress , you can extract the theme files by viuepress eject
.
$ npx vuepress eject src/.vuepress/theme/
If you want to customize the default theme, you can also create a theme which extends the default theme. But in my case, I want to customize the theme by reading the original one and I feel I'm able to customize details more efficiently.
If you eject the original theme to src/.vuepress/theme/
, VuePress automatically load the ejected files, so what you have to do is just to modify the ejected files.
# File Structure
First, I intended to create the function in which, when I put category/tags to a page on frontmatter, the pages of category/tags archives are automatically created. However, I tried and could not achieved this, finally I have to put .md files when I need a new taxonomy. Then by this customization in this article, when you add a new tags or categories to a page, you should create .md file correspondent to the added tags/categories.
In the end, you need to create or modify following files.
src/
├── .vuepress
│ ├── components
│ │ └── Taxonomies.vue(*Taxonomy list page)
│ └── theme
│ ├── components
│ │ ├── Sidebar.vue(*Sidebar)
│ │ ├── SidebarLatest.vue(*(Added)Latest articles in sidebar)
│ │ ├── SidebarLink.vue(*for each sidebar link : add category to the title text)
│ │ ├── SidebarTaxonomies.vue(*(Added)Taxonomies in sidebar)
│ │ └── Taxonomy.vue(*Taxonomy archive page)
│ └── layouts
│ └── Layout.vue(*Call Taxonomy Component in Taxonomy archive page)
├── articles(*Articles)
│ ├── README.md
│ └── (略)
├── categories(*Categories)
│ ├── README.md
│ ├── nginx.md
│ └── vuepress.md
└── tags(*Tags)
├── README.md
├── nginx.md
└── vuepress.md
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Set taxonomies to each articles
# articles directory
Put category
and tags
in the frontmatter
in pages in articles/
directory like this.
# articles/vuepress/taxonomies.md
---
title: "How to create category and tag pages on VuePress"
date: 2021-11-27T13:23:00+09:00
category: vuepress
tags:
- "vuepress"
- "customize"
---
2
3
4
5
6
7
8
This article has vuepress
category and vuepress
tags and customize
tags, then this article is listed in these taxonomy archive pages.
# taxonomy directory and taxonomy archive pages
Category and tag directories and pages are like this:
- /tags/ : Tag list
- /categories/ : Category list
- /tags/* : Tag archive pages
- /categories/* : Category archive pages
# README.md
categories/README.md
and tags/README.md
is for taxonomy list.
Put Taxonomies
component in README.md
like this.
# categories/README.md
---
title: "Categories"
---
# Categories
<Taxonomies taxonomy="category" />
2
3
4
5
6
7
# tags/README.md
---
title: "Tags"
---
# Tags
<Taxonomies taxonomy="tags" />
2
3
4
5
6
7
And Taxonomies
component will list each categories or tags.
# components/Taxonomies.vue
Most of vue files are put in .vuepress/theme/components
, but this file is created in .vuepress/components
because this component is referred from md files.
<template>
<div class="sidebar-links">
<ul class="sidebar-links">
<li v-for="post in posts" :key="post.path">
<RouterLink :to="post.path">{{ post.title }}</RouterLink>
</li>
</ul>
</div>
</template>
<script>
import moment from "moment";
export default {
name: 'Taxonomies',
props: ["taxonomy"],
data () {
return {};
},
computed: {
posts() {
const tax = this.taxonomy;
let pages = this.$site.pages.filter(post => {
// skip if `taxonomy` is not defined or defferent
if (!post.frontmatter || !post.frontmatter.taxonomy ||
post.frontmatter.taxonomy !== tax)
{
return false;
}
return true;
}).map(post => {
post._date = moment(post.frontmatter.date).format("YYYY/MM/DD");
return post;
});
pages.sort(()=> Math.random() - 0.5);
return pages;
},
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# Create Taxonomy archive pages
# .md file
As written above, you have to create each .md files correspondent to each taxonomy archive page.
The following is .md files for vuepress
category and tag.
# categories/vuepress.md
---
title: "Vue Press"
taxonomy: "category"
terms: "vuepress"
---
VuePress is a static site generator, which is also used in this site.
2
3
4
5
6
7
# tags/vuepress.md
---
title: "Vue Press"
taxonomy: "tags"
terms: "vuepress"
---
2
3
4
5
For instance, in categories/vuepress.md
, frontmatter includes taxonomy: category
and terms: vuepress
.
If you want to add categories or tags, you can create a new .md file.
# components/Taxonomy.vue
components/Taxonomy.vue
shows a list of pages with a single taxonomy.
<template>
<main class="page">
<slot name="top" />
<div class="theme-default-content">
<h2>{{tax_name}}: {{ title }}</h2>
<Content />
<ul>
<li v-for="post in posts" :key="post.path">
{{post._date}}
<RouterLink :to="post.path">
{{ post.title }}
</RouterLink>
</li>
</ul>
</div>
<slot name="bottom" />
</main>
</template>
<script>
import _orderBy from "lodash/orderBy";
import moment from "moment";
export default {
props: [],
computed: {
tax_name() {
return this.$page.frontmatter.taxonomy;
},
title() {
return this.$page.frontmatter.title;
},
posts() {
const self = this;
const taxonomy = this.$page.frontmatter.taxonomy;
const terms = this.$page.frontmatter.terms;
const posts = this.$site.pages.filter(post => {
if (typeof post.frontmatter[taxonomy] !== "undefined") {
if (Array.isArray(post.frontmatter[taxonomy])) {
return post.frontmatter[taxonomy].includes(terms);
} else {
return post.frontmatter[taxonomy] == terms;
}
}
return false;
}).map(post => {
post._date = moment(post.frontmatter.date).format("YYYY/MM/DD");
return post;
});
return _orderBy(posts, ["_date"], ["desc"]);
}
},
}
</script>
<style lang="stylus">
@require '../styles/wrapper.styl'
.page
padding-bottom 2rem
display block
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# modify layouts/Layout.vue
Then I modified Layout.vue
to call the Taxonomy
component I created above.
I already created taxonomy archive .md files and in those files I put taxonomy
in frontmatter. Layout.vue
renders the Taxonomy
component when a .md file has taxonomy
in frontmatter.
<template>
<!-- ... -->
<Home v-if="$page.frontmatter.home" />
<Taxonomy v-else-if="$page.frontmatter.taxonomy" />
<Page ...
/>
<!-- ... -->
</template>
<script>
// ...
import Taxonomy from '@theme/components/Taxonomy.vue'
export default {
// ...
components: {
// ...
Taxonomy,
},
// ...
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
And then, type yarn dev
and if http://localhost:8080/tags/vuepress
and http://localhost:8080/categories/vuepress
work correctly, we've finished taxonomy archive pages 💮
# Add tags and categories on sidebar
Now I modified following 4 files to add tags and categories on the sidebar.
In addition, I also added a list of latest posts on the sidebar.
- SidebarLink.vue(*for each sidebar link : add category to the title text)
- (Added) SidebarLatest.vue(*Latest articles in sidebar)
- (Added) SidebarTaxonomies.vue(*Taxonomies in sidebar)
- Sidebar.vue(*Sidebar)
# components/SidebarLink.vue
SidebarLink
is originally used for showing the link(s) of items on the sidebar, and I modified this file so that you can set a category
prop (boolean) to show the category of items.
export default {
props: ['item', 'sidebarDepth', 'category'],
render (h,
{
parent: {
$page,
$site,
$route,
$themeConfig,
$themeLocaleConfig
},
props: {
item,
sidebarDepth,
category, // added
}
}) {
// modified to add item.frontmatter.category
let text_ = item.title || item.path;
if (category && item.frontmatter.category) {
text_ = `[${item.frontmatter.category}] ${text_}`;
}
const link = item.type === 'external'
? renderExternal(h, item.path, text_)
: renderLink(h, item.path, text_, active)
//...
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# components/SidebarLatest.vue
SidebarLatest.vue
shows latest articles on the sidebar. You can set the category
prop true
on SidebarLink
.
<template>
<div class="sidebar-links">
<div class="sidebar-group">
<h4 class="sidebar-heading">Latest Posts</h4>
<ul
v-if="pages.length"
class="sidebar-links"
>
<li
v-for="(page, i) in pages"
:key="i"
>
<SidebarLink
:sidebar-depth="0"
:item="page"
:category="true"
/>
</li>
</ul>
</div>
</div>
</template>
<script>
import SidebarGroup from '@theme/components/SidebarGroup.vue'
import SidebarLink from '@theme/components/SidebarLink.vue'
import _orderBy from "lodash/orderBy";
import moment from "moment";
export default {
name: 'SidebarLinks',
components: { SidebarGroup, SidebarLink },
data () {
return {};
},
computed: {
pages() {
let pages = this.$site.pages.filter(post => {
if (post.frontmatter) {
if (post.frontmatter.home) return false; //home
if (post.frontmatter.taxonomy) return false; // exclude taxonomy pages
}
if (post.relativePath) {
// just under articles/
if (post.relativePath.indexOf("articles/") !== 0) {
return false;
}
// ignore README.md
if (post.relativePath.substr(-9) === "README.md") {
return false;
}
}
return true;
}).map(post => {
post._date = moment(post.frontmatter.date).format("YYYY/MM/DD");
return post;
});
pages = _orderBy(pages, ["_date"], ["desc"]);
if (pages.length > 10) pages = pages.slice(0, 10);
return pages;
},
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# components/SidebarTaonomies.vue
SidebarTaonomies.vue
shows taxonomies on the sidebar.
<template>
<div class="sidebar-links">
<div class="sidebar-group">
<RouterLink :to="root_url" class="sidebar-heading clickable" v-if="root_url">{{title}}</RouterLink>
<h4 class="sidebar-heading" v-else>{{title}}</h4>
<ul class="sidebar-links">
<li
v-for="(tag, i) in tags"
:key="i"
>
<SidebarLink
:sidebar-depth="0"
:item="tag"
/>
</li>
</ul>
</div>
</div>
</template>
<script>
import SidebarGroup from '@theme/components/SidebarGroup.vue'
import SidebarLink from '@theme/components/SidebarLink.vue'
import _orderBy from "lodash/orderBy";
import moment from "moment";
export default {
name: 'SidebarTaxonomies',
components: { SidebarGroup, SidebarLink },
props: ["taxonomy", "title", "root_url"],
data () {
return {};
},
computed: {
tags() {
const tax = this.taxonomy;
let pages = this.$site.pages.filter(post => {
// タグ指定が指定のものでないものはパス
if (!post.frontmatter || !post.frontmatter.taxonomy ||
post.frontmatter.taxonomy !== tax)
{
return false;
}
return true;
}).map(post => {
post._date = moment(post.frontmatter.date).format("YYYY/MM/DD");
return post;
});
//pages = _orderBy(pages, ["_date"], ["desc"]);
pages.sort(()=> Math.random() - 0.5);
return pages;
},
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# components/Sidebar.vue
I've already created SidebarLatest
and SidebarTaxonomies
components, then I made Sidebar
refer to these components so that you can see latest posts and lists of taxonomies (categories and tags) on the sidebar.
<template>
<aside class="sidebar">
<NavLinks />
<slot name="top" />
<template v-if="items.length > 0">
<SidebarLinks
:depth="0"
:items="items"
/>
<hr />
</template>
<slot name="bottom" />
<SidebarLatest />
<SidebarTaxonomies taxonomy="category" title="Categories" root_url="/categories/" />
<SidebarTaxonomies taxonomy="tag" title="Tags" root_url="/tags/" />
</aside>
</template>
<script>
// ...
import SidebarLatest from '@theme/components/SidebarLatest.vue'
import SidebarTaxonomies from '@theme/components/SidebarTaxonomies.vue'
export default {
// ...
components: { SidebarLatest, SidebarTaxonomies },
// ...
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Related posts
- How to Avoid Embedding Google Adsense and Analytics Code in Your Development Environment on VuePress
- How to get VuePress latest pages from outside
- How to hide sidebar navigation if there is only a single item
- How to make moment.js lighter by execluding unused locale files
- How many pages can VuePress have?
- How to construct a VuePress website