Full-text search with pre-build indexes for Nuxt.js using lunr.js
During the build phase of Nuxt.js you can add documents to the search index builder by calling the nuxt hook lunr:document
. If you have configured and pass a supported locale along the document, the index will be build using the lunr stemmer for the specified language. As the lunr search index only holds references to your documents, you can optionally pass a meta prop along with the document which will be added to the search index as well.
- Add
@nuxtjs/lunr-module
dependency to your project
$ yarn add @nuxtjs/lunr-module # or npm install @nuxtjs/lunr-module
- Add
@nuxtjs/lunr-module
to themodules
section ofnuxt.config.js
{
modules: [
// Simple usage
'@nuxtjs/lunr-module',
// With options
{
src: '@nuxtjs/lunr-module',
// These are the default options:
/*
options: {
includeComponent: true,
globalComponent: false,
css: true,
defaultLanguage: 'en',
languages: false,
path: 'search-index',
ref: 'id',
fields: [
'title',
'body'
]
} */
}
]
}
Add documents by calling the nuxt hook lunr:documents
as follows:
const document = {
id: 1,
title: 'My Title',
body: 'The body'
}
const meta = {
href: '/link/to/page',
title: document.title
}
nuxt.callHook('lunr:document', ({
locale: 'af', // optional, default en
document,
meta // optional
})
By configuring the locales options you can indicate for which supported languages a search index should be created. If you pass any other locale with a document it will be added to the defaultLanguage
search index (and a warning will be printed).
Multiple languages equals multiple search indexes
English is supported by default in Lunr, when you only have English documents you dont have to configure or pass any locale.
A locale can consist of just a language or include a territory, you can use either of them. So both just the language (e.g. nl) as including the territory (e.g. nl_NL) will work
If this module detects your project uses nuxt-i18n then the configured locales for nuxt-i18n will be automatically added. You dont have to configure the locales you use twice unless you want to use different locales (e.g. only one) for the search index.
This module includes a search component which you can use on your website. Use it's default slot to display your search results using the properties passed in the meta prop
<template>
<lunr-search :lang="lang">
<template v-slot:default="{ result, index, maxScore, meta }">
<nuxt-link :to="meta.href">
{{ meta.title }}
<span>score: {{ Math.round(100 * result.score / maxScore) }}%</span>
</nuxt-link>
</template>
</lunr-search>
</template>
<script>
export default {
components: {
// Note: 'lunr-module' below is a custom webpack alias and
// does NOT refer to the folder in node_modules
LunrSearch: () => import('lunr-module/search')
}
}
</script>
The document reference (defined by option.ref) is used in the search index to link all stemmed word segments. If you would use e.g. the page path as the document reference, then for a page with path /en/offices/emea/nl/contact
that string value will be used to link all the stemmed word segments. This could increases the size of the search index massively, therefor its recommended to create a numeric document reference and use the meta
property to add the page path for that document reference only once to the search index.
Lunr doesnt export ES6 modules and can't be tree-shaked. Using this module adds ~8.5KB
+ ~2KB
for every other language then English to your bundle (gzipped). See example above for a dynamic import which makes sure the lunr imports are not included in your vendors bundle
The search index is saved as a json file and thus needs to be parsed on the client. If the json of your search index becomes very large it could be an issue on (mobile) clients. E.g. Safari on iOS limits each top-level entry-point to run for max 10 seconds. See this Lunr.js issue for a possible workaround.
- type
Boolean
- default
true
If true then the default search component will be added to your Nuxt project
You can import the component using the webpack alias
lunr-module/search
- type
Boolean
,String
- default
false
If truthy then a plugin will be added to your project which installs the search component globally
Setting this option implies
includeComponent: true
By default the search component will be registered as lunr-search
, if you wish this name you can set this option to a string with the name you wish to use
globalComponent: 'my-global-lunr-search'
...
<template>
<my-global-lunr-search :lang="lang" class="my-search" />
</template>
- type
Boolean
- default
true
If the search component is included, this option determines if the default css styles should be included or not
- type
String
- default
'en'
The default language to use. This is also the fall-back language if you pass a document with a non-supported locale
- type
String
,Array
- default
undefined
A string or array of the languages for which a search index needs to be created.
Although this option might look redundant (i.e. why not just create a search index for every language which is passed), it provides a check to prevent 'rogue' documents from being indexed into a separate index.
- type
Object
- default
{}
An object which contains a map of unsupported languages to supported stemming languages (by lunr). Use this to specify which stemming language should be used if you want to build a search index for a language which is not supported by lunr (yet).
- type
String
- default
search-index
On build of your project the created search indexes will be emitted as webpack assets. This option determines the path under nuxt.publicPath
should be used.
With default configurations for Nuxt.js and this module the English search index is available under
/_nuxt/search-index/en.json
- type
String
- default
id
The name of the property in your document which specifies what as reference should be used for this document in the search index
- type
Array
- default
['title', 'body']
The property names in your document which will be indexed
- type
String
- default
Search
The text that is put in the placeholder of the search input field. If you are using nuxt-i18n, you can also provide a translation with key lunr-module.placeholderText
- type
Array<String>
- default
{ fetching: 'Fetching search index', loading: 'Loading search index', searching: 'Searching...', noresults: 'No results found' }
The status messages which are displayed during the search life cycle. If you are using nuxt-i18n, you can also provide a translation with key lunr-module.${key}
. Eg use lunr-module.noresults
to provide a translation for the no results found status.
use
nuxt.callHook(<name>, <arg>)
if you wish to use these hooks
- arg:
({ locale?, document, meta? })
The main hook which is used to add documents to the search index. You need to pass an object as described. Both locale as meta are optional. If no locale is passed the defaultLanguage
will be used
use
nuxt.hook(<name>, <callback>)
if you wish to use these hooks. The callback function receives the listed arguments
- arg:
({ locale, builder })
Use this hook if you e.g. want to register custom lunr plugins
This hook is called after the lunr builder is instantiated but before language plugins have been added or any refs and fields are set
- arg:
({ locale, builder })
Use this hook if you e.g. want to add extra documents to an index
This hook is called when the lunr builder has been setup, just before the documents are added to the index.
- arg:
({ locale, builder, index })
Use this hook if you wish to interact with the index before exporting it
This hook is called just before the index is exported as an json object
- arg:
({ locale, json })
Use this hook if you wish to manipulate the json which is emitted as search index for the locale
This hook is called during webpack compilation just before the search index is prepared to be emitted as asset
- Clone this repository
- Install dependencies using
yarn install
ornpm install
- Start development server using
npm run dev
Copyright (c) pimlie