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:

GIF of posts being filtered as user types query in search box

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 the foo-bar fleck. A slug value is usually calculated in your gatsby-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,              }              )              )              ,                      

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.

GIF of posts being filtered as user types query in search box

Comments

mobleyslonly.blogspot.com

Source: https://www.emgoto.com/gatsby-search/

Belum ada Komentar untuk "Gatsby Cannot Read Property 'allmarkdownremark' of Undefined"

Posting Komentar

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel