gatsby-paginate
gatsby-paginate copied to clipboard
Can I create a paginate for both index and other pages
In my blog I have the following pages:
- Index Page
- Posts
- Category
- Tags
I was able to implement pagination for the Index page but I would also like to do the same for the category and tags pages. How do I do that?
Hi @GoChartingAdmin
There is an undocumented feature (docs coming) that can do this for you. There is an option called pathPrefix
which takes a string.
Essentially you can add any array of posts as the posts
option in the options object. This function is basically a replacement for createPages()
from the main Gatsby proj although it uses that function to power page creation.
if you have an object containing all your tags and an array of posts for each, you could loop through those and apply the tag name as your pathPrefix
option, and 5 for your pageLength
option then the plugin will create a yoursite/:tagName
page containing the first 5 posts and then a yoursite/:tagName/2 page containing the next 5 etc.
Or you could add tag/${tagName}
as the prefix to give yoursite/tag/:tagName
etc
I have a little bit of work to do in extending this functionality and documenting.
Get back if that doesn't work/make sense.
I tried the below code in gatsby-node.js
I have a 1:n mapping against category to post
// Find unique list of categories across all posts
const categoryBaseList = result.data.allContentfulTestBlog.edges
.map((each)=>{return each.node.category})
.filter(function(item, i, ar){ return ar.indexOf(item) === i; });
// Looping as per your suggestion
categoryBaseList.forEach(category=>{
createPaginatedPages({
edges: result.data.allContentfulTestBlog.edges,
createPage: createPage,
pageTemplate: "src/templates/category.jsx",
pageLength: 5,
pathPrefix: `categories/${category}`
});
})
Now when I tried the below in my category component available in rc/templates/category.jsx
const { group, index, first, last } = this.props.pathContext;
console.log(group) //Response is undefined
What am I missing
Not had a chance to test anything but try passing category
to the edges arg instead of result.data
....etc.
categoryBaseList.forEach(category=>{
createPaginatedPages({
edges: category,
createPage: createPage,
pageTemplate: "src/templates/category.jsx",
pageLength: 5,
pathPrefix: `categories/${category}`
});
})
It did not work
What does categoryBaseList end up looking like before it is iterated over?
Here's a recipe that works for me:
result.data.allContentfulCategory.edges.forEach(({ node: c }) => {
const edges = result.data.allContentfulBlogPost.edges.filter(({ node }) => {
return node.categories.some(category => category.permalink === c.permalink))
})
createPaginatedPages({
edges,
createPage,
pageTemplate: './src/templates/category.js',
pageLength: 5,
pathPrefix: `blog/${c.permalink}`,
})
})
If you need more details about this, let me know.
I think I am close thanks to all your help. Below is my detailed code.
- Paginated Index page generated (as expected)
- Non-paginated tag pages generated (as expected)
- Neither paginated nor non-paginated categories pages generated (confused)
exports.createPages = ({ graphql, boundActionCreators }) => {
const { createPage } = boundActionCreators;
return new Promise((resolve, reject) => {
const postPage = path.resolve("src/templates/post.jsx");
const tagPage = path.resolve("src/templates/tag.jsx");
const categoryPage = path.resolve("src/templates/category.jsx");
resolve(
graphql(
`{
allContentfulTestBlog {
edges {
node {
id
tags
category
date
title
fields{
slug
nextSlug
nextTitle
prevSlug
prevTitle
}
html{
id
html
}
cover {
resolutions {
base64
aspectRatio
width
height
src
srcSet
}
}
}
}
}
}
`
).then(result => {
if (result.errors) {
/* eslint no-console: "off" */
console.log(result.errors);
reject(result.errors);
}
createPaginatedPages({
edges: result.data.allContentfulTestBlog.edges,
createPage: createPage,
pageTemplate: "src/templates/index.jsx",
pageLength: 5
});
const tagSet = new Set();
const categorySet = new Set();
result.data.allContentfulTestBlog.edges.forEach(edge => {
if (edge.node.tags) {
edge.node.tags.forEach(tag => {
tagSet.add(tag);
});
}
if (edge.node.category) {
categorySet.add(edge.node.category);
}
createPage({
path: edge.node.fields.slug,
component: postPage,
context: {
slug: edge.node.fields.slug,
id: edge.node.id
}
});
});
const tagList = Array.from(tagSet);
tagList.forEach(tag => {
createPage({
path: `/tags/${_.kebabCase(tag)}/`,
component: tagPage,
context: {
tag
}
});
});
const categoryList = Array.from(categorySet);
categoryList.forEach(category => {
const edges = result.data.allContentfulTestBlog.edges.filter(({ node }) => { return node.category===category });
createPaginatedPages({
edges,
createPage,
pageTemplate: "src/templates/category.jsx",
pathPrefix: `/categories/${_.kebabCase(category)}/`,
pageLength: 5,
context: { category }
});
});
})
);
});
};
Do your actual posts have categories added to them? Try logging out the item count in the set, then the length of the categoryList
array and then the set / array themselves.
Yes. Will provide by EOD
Please find the output below
categorSet_Count: undefined
categoryList_Count: 7
categorySet: Set {
'Grand opening',
'another one',
'moar',
'random',
'test3',
'tech',
'gatsby'
}
categoryList: [ 'Grand opening',
'another one',
'moar',
'random',
'test3',
'tech',
'gatsby' ]
And a sample edges which is getting created in the forEach loop under categoryList
const categoryList = Array.from(categorySet);
categoryList.forEach(category => {
const edges = result.data.allContentfulTestBlog.edges.filter(({ node }) => { return node.category===category });
createPaginatedPages({
edges,
createPage,
pageTemplate: "src/templates/category.jsx",
pathPrefix: `/categories/${_.kebabCase(category)}/`,
pageLength: 5,
context: { category }
});
});
[ { node:
{ id: 'c392JTEW93GMaIKq20QaaIY',
tags: [Array],
category: 'tech',
date: '2017-12-12T00:00+01:00',
title: 'Bold Mage',
fields: [Object],
html: [Object],
cover: [Object] } } ]
Do you still stuck at here?
I solved it
graphql(`
{
allMarkdownRemark {
edges {
node {
html
id
frontmatter {
path
title
type
date(formatString: "MMMM DD, YYYY")
tags
categories
}
}
}
}
}
`).then(result => {
if (result.errors) {
return Promise.reject(result.errors)
}
const edges = result.data.allMarkdownRemark.edges
let tags = []
let categories = []
edges.forEach(({ node }, index) => {
// Collect all tag, category and ignore duplicated
tags = Array.from(new Set([...tags, ...node.frontmatter.tags]))
categories = Array.from(new Set([...categories, ...node.frontmatter.categories]))
createPage({
path: node.frontmatter.path,
component: path.resolve('src/templates/blog.js'),
})
})
// Create paginated pages for homepage
createPaginatedPages({
edges,
createPage,
pageTemplate: path.resolve('src/templates/index.js'),
})
// With each tag, find all posts with this tag and create paginated pages
tags.forEach(tag => {
const tagEdges = edges.filter(({ node }) => node.frontmatter.tags.includes(tag))
const slug = slugify(tag) // create slug for page. I need some modify so I created my own. You can use lodash.kebabCase.
createPaginatedPages({
edges: tagEdges,
createPage,
pageLength: 1, // 1 for testing. Change to your configs
pathPrefix: `tag/${slug}`,
pageTemplate: path.resolve('src/templates/tags.js'),
context: {
name: tag, // For display tag name in page.
path: slug, // For navigation between pages
},
})
})
// Categories same as tags above
})