Hero background image
image (2)

Leading Edge - filtering front to back

A CMS solution for a client that was set up with Prismic and Gatsby. Also took advantage of Netlify for email notification and cloud hosting. The newsletter subscription was set up through Mail Chimp.

Often times you’ll have a fairly complicated assortment of resources to filter through on the front end. I’m going to walk you through our approach to filtering through a resource room with Prismic and Gatsby. If you’re new to Prismic or Gatsby be sure to check out their excellent docs to get started.

Templates

Templates to the rescue! If we structure our CMS in a thoughtful way then we can abstract away a lot of the complexity of filtering on the front-end. In Prismic you’ll need to create a repeatable content type of category and assign a category for each blog. Or in traditional back end speak there needs to be a one-to-many relationship between blogs and categories. This way we’re able to assign the data with its own designated endpoint for each category. Let’s explore how.

Here’s an example of how a blog content type could look. The essential takeaway here is including a unique uid, which we will use for our routing and dynamic page generation.

In our application, we’re going to generate templates with the help of gatsby-source-prismic-graphql

I would recommend this plugin over gatsby-source-prismic because it comes with content previewing right out of the box, which is a pretty essential feature if your client is not tech-savvy and are going to be updating the application independently.

gatsby-config.js

 {
      resolve: 'gatsby-source-prismic-graphql',
      options: {
        repositoryName: 'leading-edge',
        path: '/preview',
        previews: true,
        pages: [
          {
            type: 'Post',
            match: '/post/:uid',
            path: '/post-preview',
            component: require.resolve('./src/templates/post.js'),
          },
          {
            type: 'Person',
            match: '/bio/:uid',
            path: '/bio-preview',
            component: require.resolve('./src/templates/bio.js'),
          },
          {
            type: 'Person',
            match: '/author/:uid',
            path: '/author-preview',
            component: require.resolve('./src/templates/author.js'),
          },
          {
            type: 'Resource',
            match: '/resource/:uid',
            path: '/resource-preview',
            component: require.resolve('./src/templates/resource.js'),
          },
          {
            type: 'Topic',
            match: '/topic/:uid',
            path: '/topic-preview',
            component: require.resolve('./src/templates/topic.js'),
          },
          {
            type: 'Simple_page',
            match: '/page/:uid',
            path: '/simple-page-preview',
            component: require.resolve('./src/templates/simple-page.js'),
          },
          {
            type: 'Category',
            match: '/blog/:uid',
            path: '/category-preview',
            component: require.resolve('./src/templates/category.js'),
          },
        ],
      },
    },
    {
      resolve: 'gatsby-plugin-react-svg',
      options: {
        rule: {
          include: /vectors/,
        },
      },
    },

The pages attribute is what finds all our uid’s and dynamically creates our routes. Here’s what the post.js template could look like: 

export const query = graphql`
  query getPost($uid: String!) {
    prismic {
      postByUID: post(uid: $uid, lang: "en-us") {
        _meta {
          uid
          firstPublicationDate
        }
        title
        unlisted
        deck
        publish_date
        category {
          ... on PRISMIC_Category {
            title
            _meta {
              uid
            }
          }
        }
        authors {
          author {
            ... on PRISMIC_Person {
              _meta {
                uid
              }
              name
              short_bio
              image
              hide_bio
            }
          }
        }
        feature_image
        preview_image
        related_posts {
          post {
            ... on PRISMIC_Post {
              title
              deck
              _meta {
                uid
              }
              category {
                ... on PRISMIC_Category {
                  title
                }
              }
              feature_image
              preview_image
            }
          }
        }
        body {
          ... on PRISMIC_PostBodyText {
            type
            primary {
              content
            }
          }
          ... on PRISMIC_PostBodyPullquote {
            type
            primary {
              text
            }
          }
          ... on PRISMIC_PostBodyBlock_image {
            type
            primary {
              image
              caption
              credit
            }
          }
          ... on PRISMIC_PostBodyCustom_html {
            type
            primary {
              content
            }
          }
        }
        social_title
        social_description
        social_image
      }
    }
  }
`;

GraphQL

Our qraphQL query is set up to take a UID as a parameter and fetch the corresponding data depending on what UID is passed in. 

Now that we have all our posts rendered as their own page we can move on to the categories and define a template for those.

 gatsby-config-js

 {
            type: 'Category',
            match: '/blog/:uid',
            path: '/category-preview',
            component: require.resolve('./src/templates/category.js'),
          },

Again the important take away is the uid that we’re passing into our graphql query.

const postQuery = graphql`
  query getCategoryPosts($first: Int = 12, $after: String, $id: String!) {
    prismic {
        categoryPosts: allPosts(first: $first, after: $after, sortBy: publish_date_DESC, where: {category: $id}) {
            edges {
                node {
                    unlisted
                    deck
                    feature_image
                    publish_date
                    title
                    category {
                        ... on PRISMIC_Category {
                            title
                            description
                        }
                    }
                    preview_image
                    _meta {
                        uid
                    }
                }
            }
        }
    }
  }
`

The where keyword in graphql defines the condition for the data we are querying so the above code will define an endpoint for each category and fetch all blog posts that are under that category. 

Front End Filtering

If you have a second factor to sort by then we’ll need to take this one step further and drill into each section on the front end. 

Let’s create a select element and use the handle change to trigger a filterByType() method. There will of course have to be a type associated with each of our blog posts so make sure you have that set up in your CMS.

Here we’re taking the target from the select and beginning the filtering process:

async fetchPosts() {     this.props.prismic       .load({         variables: {           after: getCursorFromDocumentIndex(this.state.loadedPostTotal - 1)         }       })       .then(res => {         const loadedPosts = res.data.allPosts.edges;          this.setState({           loadedPostTotal: this.state.loadedPostTotal + loadedPosts.length,           posts: this.state.posts.concat(loadedPosts.filter(post => !post.node.unlisted)),           total: res.data.allPosts.totalCount,         });          return this.state.loadedPostTotal;        })       .then(newTotal => {         if (newTotal < this.state.total) {           this.fetchPosts();          } else {           this.setState({             loaded: true,             currentPosts: this.state.posts.slice(this.state.firstPost, this.state.lastPost),           });         }       })      return true;   }

Since the data is going to be a changing variable we need to first set the state with the blogs by category.

The setCurrentBlogs() method is going to asynchronously splice, depending on how many blogs we want to see per page, and set the state with the new posts. 

 async setCurrentPosts() {
    await this.setState({
      loaded: false,
    })

    await this.setState({
      currentPosts: this.state.posts.slice(this.state.firstPost, this.state.lastPost),
      loaded: true,
    })
  }

That’s pretty much it! Now you have the filtered blogs in state that are going to change dynamically in your render method in a clean and concise way. You can check out the finished product here.

Filtering a complicated resource room for a client can take a bit of planning at times, but with the right setup on the backend, graphQL, React and a bit of es6 thrown in you can get it together is a pretty readable and maintainable way. 

alt

alt

alt

alt

site logo