Gatsby Cannot Read Property 'allmarkdownremark' of Undefined
How to add search to your Gatsby site
A search bar is a not bad manner to make content on your Gatsby site discoverable. In this tutorial, I'll exist walking you through how to add local search to Gatsby with FlexSearch.
I'll exist basing the code off Gatsby's official starter weblog template, gatsby-starter-blog. We'll also exist using a React search bar component I built in a previous post.
At the end of the tutorial, you will have a search bar that allows readers to search through your content:
Choosing a search library for Gatsby
Do you demand a search library? Not ever. It is possible to write a filter that finds partial matches based off post titles. But if you have a lot of posts, or you want to search off many fields, a search library may be for you.
There are quite a few JavaScript search libraries out there that you lot can use. I chose FlexSearch due to its ease of setup. It also claims to be the fastest search library. Sounds pretty good to me!
Add a search bar component to your Gatsby site
We'll be putting our search bar on the domicile folio.
The dwelling house folio uses a GraphQL folio query to grab a list of all the posts, and then loops through and renders a link out to each mail service.
import React from 'react' ; import PostLink from '../components/post-link' ; export default ( { information: { allMarkdownRemark: { nodes } , } , } ) => { const posts = nodes; return ( <div > <h1 > Web log </h1 > {posts. map ( post => // PostLink will exist a component that renders a summary of your post // east.yard. the title, appointment and an extract < PostLink mail service = {post} /> ) } </div > ) ; } ; export const pageQuery = graphql ` query { allMarkdownRemark(sort: { fields: [frontmatter___date], order: DESC }) { nodes { excerpt fields { slug } frontmatter { date(formatString: "MMMM DD, YYYY") title } } } } `
Create a carve up search.js
file to store your search bar component:
import React from 'react' ; const SearchBar = ( { searchQuery, setSearchQuery } ) => ( <class action = "/" method = "go" autoComplete = "off" > <label htmlFor = "header-search" > <bridge className = "visually-hidden" > Search blog posts </bridge > </label > <input value = {searchQuery} onInput = { ( e ) => setSearchQuery (e.target.value) } type = "text" id = "header-search" placeholder = "Search blog posts" proper noun = "s" /> <button blazon = "submit" > Search </button > </form > ) ;
Equally well as some CSS to hide our screen reader-friendly label:
.visually-hidden { clip : rect (0 0 0 0) ; clip-path : inset (50%) ; height : 1px; overflow : hidden; position : absolute; white-space : nowrap; width : 1px; }
I've written a separate mail going into detail on how to create an accessible search bar component.
Then on our abode page we can add this new component:
import React from 'react' ; import Search from '../components/search' ; import './alphabetize.css' ; export default ( { data: { allMarkdownRemark: { nodes } , } , } ) => { const { search } = window.location; const query = new URLSearchParams (search) . get ( 's' ) const [searchQuery, setSearchQuery] = useState (query || '' ) ; const posts = nodes; render ( <div > <h1 > Blog </h1 > < SearchBar searchQuery = {searchQuery} setSearchQuery = {setSearchQuery} /> {posts. map ( post => ( < PostLink post = {post} /> ) ) } </div > ) ; } ;
Now, you'll take a search bar set up on your Gatsby site.
Install gatsby-plugin-local-search and FlexSearch
At present that we have our search bar, we'll need to hook it upward to a search library.
The Gatsby ecosystem has plugins for every occassion - and search is no exception!
Beginning, install gatsby-plugin-local-search:
yarn add together gatsby-plugin-local-search # or npm install gatsby-plugin-local-search
This plugin handles integrating your Gatsby site with a search engine library. On top of this plugin, nosotros'll also need to install our search library, FlexSearch:
yarn add flexsearch react-utilise-flexsearch # or npm install flexsearch react-apply-flexsearch
We're besides installing a react-use-flexsearch hook, which will go far easier to utilize FlexSearch later.
Update your Gatsby config file
Every bit with all Gatsby plugins, once you have installed the plugin you will need to add it to your Gatsby config file.
plugins: [ { resolve: 'gatsby-plugin-local-search' , options: { name: 'pages' , engine: 'flexsearch' , query: /** TODO **/ , ref: /** TODO **/ , index: /** TODO **/ , store: /** TODO **/ , normalizer: /** TODO **/ , } } ,
I've left most of the options blank, since these are going to be private to your site. Nosotros'll be covering them ane-by-i below.
Calculation the query value
The first value we need to add to our plugin options is the query
. This GraphQL query needs to grab the data for all your posts. This is the same query that we used earlier on the abode page of our Gatsby site:
query: ` query { allMarkdownRemark(sort: { fields: [frontmatter___date], guild: DESC }) { nodes { excerpt fields { slug } frontmatter { appointment(formatString: "MMMM DD, YYYY") title } } } } `
Choosing a ref value
The ref
is a value unique to each blog post. If your posts have unique slugs, you tin can use that.
💡 What is a slug?
If you have a mail service living at the URL
website.com/foo-bar
, the slug is thefoo-bar
fleck. A slug value is usually calculated in yourgatsby-node.js
file.
If your site doesn't have slugs, GraphQL provides an ID for each of your posts, then you lot can utilise that for your ref:
query { allMarkdownRemark( sort : { fields : [frontmatter___date] , order : DESC } ) { nodes { id
Calculation an alphabetize value
Our next value is the index
. This is the array of values that you lot want FlexSearch to search from. The most likely thing you lot'll be calculation is the title
, but you might also want users to search the postal service's excerpt or tags too.
index: [ 'championship' , 'excerpt' ]
Calculation a shop value
Next is the store
. When FlexSearch returns search results, this is the data yous want in those results. For example if you're going to return the date nether every mail, you'll want the date value.
You lot'll as well need to include in the shop your ref and index values also.
store: [ 'championship' , 'excerpt' , 'engagement' , 'slug' ]
Calculation a normalizer value
The final step is the normalizer
. FlexSearch expects all the values that y'all listed above in the shop
to be returned in a flat shape similar this:
{ title: 'Foo' , excerpt: 'Blah blah salted duck eggs' engagement: '2020-01-01' , slug: 'foo-bar' }
We need a function that volition transform the information from our GraphQL query into the expected shape:
normalizer : ( { data } ) => information.allMarkdownRemark.nodes. map ( node => ( { title: node.frontmatter.title, extract: node.extract, appointment: node.frontmatter.appointment, slug: node.fields.slug, } ) ) ,
Add your FlexSearch engine to your search bar
At present that nosotros've ready FlexSearch, we tin finally start using information technology for our search bar.
import React, { useState } from 'react' ; import { graphql } from 'gatsby' ; import { useFlexSearch } from 'react-use-flexsearch' ; export default ( { information: { localSearchPages: { alphabetize, store } , allMarkdownRemark: { nodes } , } , } ) => { const { search } = window.location; const query = new URLSearchParams (search) . go ( 's' ) ; const [searchQuery, setSearchQuery] = useState (query || '' ) ; const posts = nodes; const results = useFlexSearch (searchQuery, alphabetize, store) ; return ( <div > <h1 > Blog </h1 > < Search searchQuery = {searchQuery} setSearchQuery = {setSearchQuery} /> {posts. map ( post => ( < LinkComponent mail = {post} /> ) ) } </div > ) ; } ; export const pageQuery = graphql ` query { localSearchPages { index shop } allMarkdownRemark(sort: { fields: [frontmatter___date], lodge: DESC }) { nodes { excerpt fields { slug } frontmatter { date(formatString: "MMMM DD, YYYY") championship } } } } `
Make sure to un-normalize the data
The results
returned from the FlexSearch hook are going to be in a "apartment" shape like this:
{ title: 'Foo' , tags: [ 'tag' ] , date: '2020-01-01' , slug: 'foo-bar' }
Our link component will exist expecting the mail to be the same shape as what our GraphQL query returns. So we can write a role to put this data dorsum into its expected shape:
consign const unFlattenResults = results => results. map ( post => { const { date, slug, tags, title } = post; render { slug, frontmatter: { championship, appointment, tags } } ; } ) ;
And now we can utilise our results value:
const results = useFlexSearch (searchQuery, index, store) ; const posts = unflattenResults (results) ; return ( < > <h1 > Weblog </h1 > < Search searchQuery = {searchQuery} setSearchQuery = {setSearchQuery} /> {posts. map ( post => ( < LinkComponent post = {mail} /> ) ) } </ > ) ;
Accounting for an empty query
The FlexSearch engine volition return no results if you have an empty query. The behaviour that you want here instead is to show all the results.
When the search query is empty, we can fall back to using the original information we were getting from our GraphQL query.
const results = useFlexSearch (searchQuery, index, store) ; // If a user has typed in a query, employ the search results. Otherwise, use all posts const posts = searchQuery ? unflattenResults (results) : nodes; return ( < > <h1 > Blog </h1 > < Search searchQuery = {searchQuery} setSearchQuery = {setSearchQuery} /> {posts. map ( mail service => ( < LinkComponent post = {post} /> ) ) } </ > ) ;
At present, you volition have finished setting up the search bar set up on your Gatsby site! With search implemented, your readers can now wait for the content that is most relevant to them.
Comments
Source: https://www.emgoto.com/gatsby-search/
Belum ada Komentar untuk "Gatsby Cannot Read Property 'allmarkdownremark' of Undefined"
Posting Komentar