Search function for a Prismic.io / NuxtJS blog

After the integration of a tag system into our NuxtJS/Prismic.io blog, now comes another important function for finding content in a blog, the search.

Fortunately, Prismic.io’s API offers a full text search, so this feature can be integrated into the blog quite easily.

In the Prismic editor, we don’t have to change or add anything.

Search bar

For the search bar we create a component under “/components/SearchForm.vue”. This component can then be integrated into page, header, footer or sidebar.

For the search query we use a simple HTML form. For the sake of clarity I have removed the classes and the icon for now. The complete code is available on Github.

 
  <form
    action="/search">
    <input
      type="search"
      name="search"
      id="search"
      placeholder="Search"
      v-model="searchinput"
      class="w-full outline-none appearance-none focus:outline-none active:outline-none"
    />
    <button
      type="submit"
      class="ml-1 text-gray-400 outline-none hover:text-black focus:outline-none active:outline-none"
    ></button>
  </form>

After entering the search term and pressing the submit button, the visitor is redirected to example.com/search?search=”xxx”. The URL contains the search term directly.

Search results

For the presentation of the search results we now create “/pages/Search.vue”.

The query for Prismic is quite simple. The parameter from the URL is used as the search word. In order not to also get the tags as a result, the search is limited to the article type “Post”. Sorting is done in descending order by creation date and the result is limited to 9 items.

 
  // Query for keyword
      const searchresult = await $prismic.api.query(
        [
          $prismic.predicates.at('document.type', 'post'),
          $prismic.predicates.fulltext('document', query.search)
        ],
        { orderings: '[document.first_publication_date desc]', pageSize: 9 }
      )

The array of results will then be displayed via the GridPost component, as on the start page and overview page of the tags.

As already on the start page, a pagination, i.e. the division into several sections, is also built in here. In addition, the search word and a new search field are displayed.

The complete page looks like this:

 
<template>
  <main class="col-start-2 col-end-12">
    <div class="mb-16 text-2xl Search">
      <div>
        <strong>Results for:</strong>
        {{searchterm}}
      </div>
      <div class="w-1/12 mt-2 border-b-4 border-gray-400"></div>
    </div>

    <div v-if="result.length > 0" class="grid grid-cols-12 row-gap-16 md:col-gap-16">
      <div v-for="post in result" :key="post.id" class="col-span-12 md:col-span-4">
        <GridPost :postdata="post" :imgsize="'(min-width: 768px) 33vw, 90vw'" />
      </div>

      <div class="flex col-span-12 mt-16 loadmore">
        <button
          class="px-8 py-2 mx-auto text-lg text-center border-2 border-black border-solid cursor-pointer dark-mode:border-white hover:border-accent-dark"
          @click="loadMoreResults()"
          v-if="result.length % 9 === 0 && !nonewposts"
        >Load more results</button>
        <button
          v-else
          class="px-8 py-2 mx-auto text-lg text-center border-2 border-black border-solid cursor-pointer dark-mode:border-white"
        >You reached the bottom!</button>
      </div>
    </div>

    <div v-else class="text-xl font-semibold">Sorry, no result</div>

    <div class="w-2/3 mx-auto my-16 md:w-1/2 searchform">
      <div class="mb-4">
        <div class="text-2xl">Change your search</div>
        <div class="w-1/12 mt-2 border-b-4 border-gray-400"></div>
      </div>
      <SearchForm :currentinput="searchterm" />
    </div>
  </main>
</template>

<script>
import GridPost from '~/components/GridPost'
import SearchForm from '~/components/SearchForm'

export default {
  name: 'Search',
  components: {
    GridPost,
    SearchForm
  },
  data() {
    return {
      currentpage: 1,
      nonewposts: false
    }
  },
  async asyncData({ $prismic, error, query }) {
    try {
      // Query for keyword
      const searchresult = await $prismic.api.query(
        [
          $prismic.predicates.at('document.type', 'post'),
          $prismic.predicates.fulltext('document', query.search)
        ],
        { orderings: '[document.first_publication_date desc]', pageSize: 9 }
      )

      // Returns data to be used in template
      return {
        result: searchresult.results,
        searchterm: query.search
      }
    } catch (e) {
      // Returns error page
      error({ statusCode: 404, message: 'Page not found' })
    }
  },
  methods: {
    async loadMoreResults() {
      try {
        // Query other page for search
        const searchresult = await this.$prismic.api.query(
          [
            this.$prismic.predicates.at('document.type', 'post'),
            this.$prismic.predicates.fulltext('document', this.searchterm)
          ],
          {
            orderings: '[document.first_publication_date desc]',
            pageSize: 9,
            page: this.currentpage + 1
          }
        )

        if (searchresult.results.length > 0) {
          // Merge with the other posts
          this.result = this.result.concat(searchresult.results)
        } else {
          // No more new posts
          this.nonewposts = true
        }

        // Save current page
        this.currentpage++
      } catch (e) {
        console.error(e)
      }
    }
  }
}
</script>
search-results-prismic-nuxtjs

The next part of this tutorial series is about adding similar articles under an article.