This is the second post explaining how I implemented paged posts and tags by category on this Gatsby site using gatsby-pagination. In Part 1 I showed most of the structure of gatsby-node.js and how to create pages using Gatsby’s createPage action creator and gatsby-pagination’s createPaginationPages function.
Now let’s take a look at the category and tag pages templates.
In the previous post I showed the filter from the graphql query below. Following is the remainder of the relevent working code from the category template. There is not much here really other than the query itself. The bulk of the work is done by the CategoryTemplateDetails
component explained the next section.
class CategoryTemplate extends React.Component {
render() {
const { category } = this.props.pathContext;
return (
<div>
<Helmet title={`${category}`} />
<CategoryTemplateDetails {...this.props} />
</div>
);
}
}
export default CategoryTemplate;
export const pageQuery = graphql`
query CategoryPage($nodes: [String]) {
allMarkdownRemark(
filter: { fields: { ids: { in: $nodes } } },
sort: { order: DESC, fields: [frontmatter___date] }
){
edges {
node {
id
excerpt(pruneLength: 250)
fields {
categoryPath
}
frontmatter {
path
title
date
category
cover
}
}
}
}
}
`;
Below is category template component. All the information needed for paging comes from props.pathContent
. These are in turned passed to the Pagination component. The posts come through from the template as props.data.allMarkdownRemark.edges
. Once we have all that data the only thing left to do is render each of the post extracts which is done with the Post
component. The Post
component is used here, in the TagDetailsTemplate
and in the search page.
class CategoryTemplateDetails extends React.Component {
render() {
const { category, page, prev, next, pages, total } = this.props.pathContext;
const posts = this.props.data.allMarkdownRemark.edges;
const items = [];
posts.forEach((post) => {
items.push(<Post data={post} key={post.node.id} />);
});
return (
<div className="page">
<div className="page_subheader">
{ total } post{ total === 1 ? '' : 's' } in
</div>
<h1 className="page__title">{ category }</h1>
<div className="page__body">
{items}
</div>
<div className="page__pagination">
<Pagination
page={page}
pages={pages}
prev={prev}
next={next}
total={total}
prevText="<< Newer Posts"
nextText="Older Posts >>"
/>
</div>
</div>
);
}
}
In the tag template we are rendering one of two different results based on the input. If tag has a value then this page should be rendering all the posts for that tag. We will do that with the TagTemplateDetails
component. If tag does not have a value then we are rendering the tags in the category. We will do that with the TagsTemplateDetails
component.
class TagTemplate extends React.Component {
render() {
const { tag, category } = this.props.pathContext;
if (tag) {
return (
<div>
<Helmet title={`All Posts tagged as "${tag}"`} />
<TagTemplateDetails {...this.props} />
</div>
);
} else {
return (
<div>
<Helmet title={`Tags in ${category}`} />
<TagsTemplateDetails {...this.props} />
</div>
);
}
}
}
export default TagTemplate;
export const pageQuery = graphql`
query TagPage($nodes: [String]) {
allMarkdownRemark(
filter: { fields: { ids: { in: $nodes } } },
sort: { order: DESC, fields: [frontmatter___date] }
){
edges {
node {
id
excerpt(pruneLength: 250)
fields {
categoryPath
}
frontmatter {
title
path
date
category
cover
}
}
}
}
}
`;
The TagTemplateDetails components is very similar to the CategoryTemplateDetails component above.
class TagTemplateDetails extends React.Component {
render() {
const { tag, page, prev, next, pages, total } = this.props.pathContext;
const items = [];
const posts = this.props.data.allMarkdownRemark.edges;
posts.forEach((post) => {
items.push(<Post data={post} key={post.node.id} />);
});
return (
<div className="page">
<h1 className="page__title">
{ total } post{ total === 1 ? '' : 's' } tagged as "{tag}"
</h1>
<div className="page__body">
{items}
</div>
<div className="page__pagination">
<Pagination
page={page}
pages={pages}
prev={prev}
next={next}
total={total}
prevText="<< Newer Posts"
nextText="Older Posts >>"
/>
</div>
</div>
);
}
}
The TagsTemplateDetails is a bit different from the previous two examples because instead of displaying posts it is instead displaying tags. However, the concepts are the same. Everything we need for paging the tags in the category is included in the props.pathContext
with nodes
being the array of tags to render on the page.
class TagsTemplateDetails extends React.Component {
render() {
const { category, nodes, page, pages, prev, next, total } = this.props.pathContext;
return (
<div className="page">
<h1 className="page__title">Tags in { category }</h1>
<div className="page__body">
{ nodes.map((tagName) => {
return (
<div className="post">
<h2 className="post__title">
<Link className="post__title-link" to={`/${_.kebabCase(category)}/tags/${tagName}`}>{tagName}</Link>
</h2>
</div>
);
})}
</div>
<div className="page__pagination">
<Pagination
page={page}
pages={pages}
prev={prev}
next={next}
total={total}
/>
</div>
</div>
);
}
}
That is it for the templates themselves. In the next post we will examine the Pagination component.