Home > Net >  How do I turn a tag list into a component?
How do I turn a tag list into a component?

Time:06-20

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>;
}
  • Related