This is a continuation of Getting started with NuxtJS / Content module.
Let's get right down into it and add an Algolia search component to our Nuxt/VueJS documentation site project.
Install Algolia vue-instantsearch
In the package.json file, let's add the algoliasearch
and vue-instantsearch
dependencies.
And, since we're here, let's also add nuxt-content-algolia
and v-outside-click
as we'll also use these packages while crafting the search functionality.
"dependencies": {
...
"algoliasearch": "^4.8.3",
"vue-instantsearch": "^3.4.3",
"nuxt-content-algolia": "^0.2.0",
"v-click-outside": "^3.1.2",
"remove-markdown": "^0.3.0"
},
Next, in the project's root directory, run the yarn install
command to install the newly defined packages.
We need to now make some updates to the nuxt.config.js file.
plugins: [
'~/plugins/vue-instantsearch'
],
build: {
transpile: ['vue-instantsearch', 'instantsearch.js/es']
},
nuxtContentAlgolia: {
appId: process.env.ALGOLIA_APP_ID,
apiKey: process.env.ALGOLIA_API_KEY,
paths: [
{
name: 'documentation',
index: 'docs',
fields: ['title', 'description', 'bodyPlainText']
}
]
},
hooks: {
'content:file:beforeInsert': (document) => {
const removeMd = require('remove-markdown');
if (document.extension === '.md') {
document.bodyPlainText = removeMd(document.text);
}
},
},
We added quite a few new items to the Nuxt config file, so let's go through what they are.
plugins/vue-instantsearch is Algolia's library of pre-built Vue components that we will leverage.
transpile is needed in order for the Vue/Nuxt to understand the javascript version that Algolia uses. Make sure you define this, as you'll get a bunch of console errors otherwise!
nuxtContentAlgolia is a plugin that will index the defined contents
folders and send corresponding tag data to Algolia during yarn generate
- which is the command that will run when the site it build for static production hosting.
hooks have been added to send the markdown file plain body text with markdown characters stripped out to Algolia so that we can display body search results without any ugly html tags or markdown characters.
Now, we'll add the vue-instantsearch.js file to the plugins
directory.
import vue from 'vue';
import InstantSearch from 'vue-instantsearch'
vue.use(InstantSearch);
Alrighty, we now have our setup complete so we can start creating the search component!
Display search results
In the Search.vue file, we'll drop in vue-instantsearch components to handle search queries and display results.
We'll first want to import the necessary module.
import algoliasearch from 'algoliasearch/lite';
Then, define searchClient
and include the app id and search api id for our Algolia account.
data () {
return {
searchClient: algoliasearch('appID', 'searchApiID'),
}
},
Now, we can drop the vue-instantsearch components into the <template>
section.
Check out the Search.vue file on GitHub and watch the above video to view the components that are being added and to learn what they do. There are quite a few and might be easier to view than read in this case. 😉
Focus search box with 'command + k'
Add a keyboard command to put the search box in focus is all the rage nowadays.
Luckily, javascript is making it easier to detect and we don't have to worry about figuring out key values to refer to anymore. 🎉
On the search input field, we have a reference - ref="searchInput"
that we will use to put into focus if certain keys are pressed.
To define the keys, we'll need to add an event listener to pick up the events. Let's add this as a mounted function.
mounted: function () {
this.$nextTick(function () {
window.addEventListener('keydown', event => {
if(event.metaKey && event.key === 'k') {
this.$refs.searchInput.focus()
event.preventDefault()
}
})
})
},
You may have noticed that we also want to prevent default actions. Some browser, ahem Firefox, will put the browser search into view if you press command + k on your keyboard.
You'll also notice the metaKey
. This is either the command key if you are using a Mac device or the windows key if you're using a Windows device.
We can even detect what device type a user is using and define placeholder text based on that.
computed: {
searchPlaceholder () {
if (navigator.appVersion.indexOf('Mac') !== -1 ) {
return 'Search - ⌘k to focus'
}
else if (navigator.appVersion.indexOf('Win') !== -1 ) {
return 'Search - Win + k to focus'
} else {
return 'Search'
}
}
},
Close search results on outside click
We installed a package earlier to help us detect outside clicks.
Let's use that to close the search results dropdown when clicked outside of the search area.
First, we'll import the package to the search.vue file.
import vClickOutside from 'v-click-outside'
And then, add it as a directive.
export default {
directives: {
ClickOutside: vClickOutside.directive
},
}
We'll add this to the ais-autocomplete
component and then refer to a method on outside click events.
<ais-autocomplete v-click-outside="onClickOutside">
We have a data item labeled showResults
which is tied to v-show
on the search results dropdown. This will determine if the results display or not. On an outside click, we want to set this to false.
methods: {
onClickOutside () {
this.showResults = false
},
}
Add keyboard commands to move through results and to go to result page on click of enter key
Let's add some more delights for our keyboard-first users.
We'll add some more click handlers on the <input>
tag.
<input
...
@keydown.up.prevent="highlightPrevious"
@keydown.down.prevent="highlightNext(indices[0].hits.length)"
@keydown.enter="goToDoc(indices)"
>
We also want to add a highlight effect if a user clicks the up/down arrows as well as if the user hovers over a search result.
Let's add :class="{ 'bg-blue-900' : isCurrentIndex(index) }"
to <nuxt-link>
tag that highlights the current selected index.
And, add @mouseover="highlightedIndex = index"
to the child div of <nuxt-link>
, which will handle the mouse over and change index based on the index of the result that is currently being moused over.
Lastly, the we'll add the logic in the form of new methods to the <script>
section.
highlightPrevious () {
if (this.highlightedIndex > 0 ){
this.highlightedIndex -= 1
}
},
highlightNext (resultsCount) {
if (this.highlightedIndex < resultsCount - 1) {
this.highlightedIndex += 1
}
},
isCurrentIndex (index) {
return index === this.highlightedIndex
},
goToDoc (indices) {
this.$nuxt.$router.push(indices[0].hits[this.highlightedIndex].objectID)
}
You now have a fully functional site documentation search using Algolia and VueJS! 🔥
Following along? View the GitHub Repo to view the code for this tutorial.
You can also view the full video tutorial series playlist on YouTube.
Looking for a tool to manage your VPS servers and app deployments? Check us out at cleavr.io
Full tutorial series on creating a documentation site using Vue/Nuxt, Tailwind, and Algolia.
Top comments (1)
Excellent tutorial, thanks for sharing Adam!