Could you please tell me how to turn a page with a list of all tags into a component that would be displayed on all pages? In this case, when you click on each tag, the page redraws and displays a list of posts with a specific tag.
In the current version of the site, the page with all tags, the tag page and the page with all posts are 3 different pages.
The clearest example of how I want to end up doing this is the https://colorlibhub.com/sparkling/ site (a free theme for Wordpress). There's a block with categories on the side, click on the one you want - the page displays the posts of that category.
gatsby-node.js
const path = require('path');
const _ = require('lodash');
exports.createPages = async ({ actions, graphql, reporter }) => {
const { createPage } = actions;
const blogPostTemplate = path.resolve('./src/templates/post.js');
const tagTemplate = path.resolve('./src/templates/tags.js');
const result = await graphql(`
{
postsRemark: allMdx(sort: { order: DESC, fields: [frontmatter___date] }, limit: 2000) {
edges {
node {
slug
frontmatter {
tags
url
}
}
}
}
tagsGroup: allMdx(limit: 2000) {
group(field: frontmatter___tags) {
fieldValue
}
}
}
`);
// handle errors
if (result.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`);
return;
}
const posts = result.data.postsRemark.edges;
// Create post detail pages
posts.forEach(({ node }) => {
createPage({
path: node.frontmatter.url,
component: blogPostTemplate,
context: { url: node.frontmatter.url },
});
});
// Extract tag data from query
const tags = result.data.tagsGroup.group;
// Make tag pages
tags.forEach((tag) => {
createPage({
path: `/tags/${_.kebabCase(tag.fieldValue)}/`,
component: tagTemplate,
context: {
tag: tag.fieldValue,
},
});
});
};
single tag page
import React from 'react';
import PropTypes from 'prop-types';
import { Link, graphql } from 'gatsby';
import Header from '../components/header';
import Footer from '../components/footer';
import * as styles from './tags.module.scss';
const Tags = ({ pageContext, data }) => {
const { tag } = pageContext;
const { edges, totalCount } = data.allMdx;
const tagHeader = `${totalCount} ${totalCount === 1 ? 'запись' : 'записи'} tagged with "${tag}"`;
return (
<>
<Header />
<main className={`${styles.container} ${styles.page}`}>
<h1>{tagHeader}</h1>
<ul>
{edges.map(({ node }) => {
const { slug } = node;
const { title, url } = node.frontmatter;
return (
<li key={slug}>
<Link to={`/${url}`}>{title}</Link>
</li>
);
})}
</ul>
<Link to="/tags" className={styles.button}>
All tags
</Link>
</main>
<Footer />
</>
);
};
Tags.propTypes = {
pageContext: PropTypes.shape({
tag: PropTypes.string.isRequired,
}),
data: PropTypes.shape({
allMdx: PropTypes.shape({
totalCount: PropTypes.number.isRequired,
edges: PropTypes.arrayOf(
PropTypes.shape({
node: PropTypes.shape({
frontmatter: PropTypes.shape({
title: PropTypes.string.isRequired,
}),
fields: PropTypes.shape({
slug: PropTypes.string.isRequired,
}),
}),
}).isRequired,
),
}),
}),
};
export default Tags;
export const pageQuery = graphql`
query ($tag: String) {
allMdx(
limit: 2000
sort: { fields: [frontmatter___date], order: DESC }
filter: { frontmatter: { tags: { in: [$tag] } } }
) {
totalCount
edges {
node {
slug
frontmatter {
title
url
}
}
}
nodes {
frontmatter {
title
url
}
}
}
}
`;
all tags page
import React from 'react';
import PropTypes from 'prop-types';
import kebabCase from 'lodash/kebabCase';
import { Helmet } from 'react-helmet';
import { Link, graphql } from 'gatsby';
import Header from '../../components/header';
import Footer from '../../components/footer';
import * as styles from './tags.module.scss';
const TagsPage = ({
data: {
allMdx: { group },
site: {
siteMetadata: { title },
},
},
}) => (
<>
<Helmet title={title} />
<Header />
<main className={`${styles.container} ${styles.page}`}>
<h1>Tags</h1>
<ul>
{group.map((tag) => (
<li key={tag.fieldValue}>
<Link to={`/tags/${kebabCase(tag.fieldValue)}/`}>
{tag.fieldValue} ({tag.totalCount})
</Link>
</li>
))}
</ul>
</main>
<Footer />
</>
);
TagsPage.propTypes = {
data: PropTypes.shape({
allMdx: PropTypes.shape({
group: PropTypes.arrayOf(
PropTypes.shape({
fieldValue: PropTypes.string.isRequired,
totalCount: PropTypes.number.isRequired,
}).isRequired,
),
}),
site: PropTypes.shape({
siteMetadata: PropTypes.shape({
title: PropTypes.string.isRequired,
}),
}),
}),
};
export default TagsPage;
export const pageQuery = graphql`
query {
site {
siteMetadata {
title
}
}
allMdx(limit: 2000) {
group(field: frontmatter___tags) {
fieldValue
totalCount
}
}
}
`;
blog page (list of all posts)
import * as React from 'react';
import { Link, graphql } from 'gatsby';
import Layout from '../../components/layout';
import Seo from '../../components/seo';
import Card from '../../components/Card';
import * as blog from '../../components/blog.module.scss';
function BlogPage({ data }) {
return (
<Layout>
<Seo title="Статьи" />
<main className={`${blog.container} ${blog.blog}`}>
{data.allMdx.nodes.map((post) => (
<Card
key={post.id}
category={post.frontmatter.tags.map((tag) => {
return (
<Link key={post.id} to={`/tags/${tag}`} className={blog.categoryLink}>
{tag}
</Link>
);
})}
title={post.frontmatter.title}
description={post.frontmatter.description}
link={`/${post.frontmatter.url}`}
/>
))}
</main>
</Layout>
);
}
export const PostsQuery = graphql`
query {
allMdx(sort: { fields: frontmatter___date, order: DESC }) {
nodes {
frontmatter {
title
description
date(formatString: "DD.MM.YYYY")
tags
url
}
id
body
slug
}
}
}
`;
export default BlogPage;
CodePudding user response:
Since page queries (like the one in TagsPage
) only works in pages (or in top-level components) you can't just copy and paste your query into a component because it won't be a page anymore.
Your only chance given the scenario is to use a static query to get all tags and display them into your component. There's a limitation using static queries: they don't accept dynamic parameters, since the TagsPage
query is not using any filters (as $tag
in Tags
is) it won't be a problem.
Something like this should work:
import React from "react";
import { useStaticQuery, graphql } from "gatsby";
export default function YourComponent() {
const data = useStaticQuery(graphql`
query {
allMdx(sort: { fields: frontmatter___date, order: DESC }) {
nodes {
frontmatter {
title
description
date(formatString: "DD.MM.YYYY")
tags
url
}
id
body
slug
}
}
}
`);
console.log("your tags are:", data);
return <div> your loop here</div>;
}