Search the Static
We built our website on Jekyll. Jekyll is great for developers and we love it. The blog posts we release have become a big source of reference for many in the team. As the number of posts grow, referring back to a post meant scrolling through the pages to get the posts. Not a very good experience. We needed a search. Static site generator(SSG) such as Jekyll, Gatsby,Gridsome and Hugo are a great tool to build static websites. They make it easy to build and deploy sites with Zero or extremely low costs. However, search is one of the features not supported out of the box.
Adding search functionality to a Jekyll site or whichever SSG generator you are using is no difficulty. Let’s explore some tools that we can use.
-
Lunr.js is a blazing fast simple full-text search engine for client-side applications. It’s designed to be small, yet full-featured, enabling you to provide a great search experience without the need for external, server-side, search services
-
Google Custom Search Engine enables you to create a search engine for your website, or blog. It allows the configuration of search results with images. You can fine-tune the ranking, add your promotions and customize the look and feel of the search results.
-
Jekyll simple search is a lightweight search library built on javascript. It operates entirely on the client-side and no server-side required.
-
Algolia is a reliable SAAS platform for building search into your website. It is billed to power billions of queries for thousands of companies every year and delivering results within 100ms. Impressive!
Given all the options, we decided to try out Algolia for adding a search to our blog because it seems powerfully optimized and has a pretty generous free community plan, perfect for our small website.
Why Algolia?
Speed is a critical part of keeping users happy. Algolia claims the following; It is aggressively designed to reduce latency. In a benchmarking test, Algolia returned results up to 200x faster than Elasticsearch. Algolia infrastructure is distributed around 6 regions in the world with around 47 datacentres proving a 99.99% guarantee. The goal of JAMstack is to eliminate server dependencies why add one when you can use algolia free community plan
How Algolia works
Algolia provides a REST API to query and update your search indices. All input and output is provided in JSON, making it extremely easy to use in frontend Javascript. To create, update, and maintain an Algolia search index, you’ll need to generate a valid JSON array of all of the content in your Jekyll site.
Implementing Algolia in Jekyll
To implement Algolia search in Jekyll we will use jekyll-algolia, a Jekyll plugin maintained by Algolia team, which will help us generate a JSON array and connect with Algolia search
Lets get started:-
Installation
Add jekyll-algolia
in your Gemfile
source 'https://rubygems.org'
gem 'jekyll', '~> 3.6'
group :jekyll_plugins do
gem 'jekyll-algolia'
end
Then run, bundle install
to update dependencies
Configuration
You will need to provide Algolia credentials to index your site. Open a free community plan. Once signed in you can get the API keys. Once you have your credentials, you should define your application_id
and index_name
inside your _config.yml
file like this:
# _config.yml
algolia:
application_id: your_application_id
index_name: mywebsite.com # replace with the name of your index (index database)
Usage
Once you add the application_id and index_name run below command to index your site.
ALGOLIA_API_KEY='your_admin_api_key' bundle exec jekyll algolia
Note: that env variable ALGOLIA_API_KEY should be set to the value of your Algolia admin API key. This key has write access to your index so will be able to push new data. Keep it out of git by setting it in the command or if using docker, as an env variable. If using CI/CD, ALGOLIA_API_KEY in the pipeline and then run the command
bundle exec jekyll algolia
after deploying your website.You want to keep this key secret and not commit it to your versioning system.
Below is a sample output
Configuration file: /path_to_jekyll_site/_config.yml
Processing site...
Jekyll Feed: Generating feed for posts
Rendering to HTML (100%) |===================================================|
Extracting records (100%) |===================================================|
Settings are already up to date.
Getting a list of existing records
Content is already up to date.
✔ Indexing complete
You might want to exclude indexing of certain pages on your site. To achieve this, define pages to exclude in _config.yml
file.
algolia:
application_id: your_application_id
index_name: your_app_name
search_only_api_key: your_search_only_key
files_to_exclude:
- index.html
- index.md
- _layouts/blog.html
- _layouts/default.html
Frontend
Building frontend that allows users to do the actual search is not part of the jekyll-algolia
plugin. The best solution is to use instantSearch.js library which makes it easy to design perfect search experience using prepackaged widgets.
Implementing instant search
Instant search is meant to be used with Algolia, so the API credentials to an algolia index is needed
Install instantSearch.js
You can install instantSearch.js
through CDN
or a dependencies management system(YARN/NPM
)
Form CDN
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/instantsearch.js@2.10.4/dist/instantsearch.min.css">
<script src="https://cdn.jsdelivr.net/npm/instantsearch.js@2.10.4"></script>
From NPM
npm install instantsearch.js --save
# OR
yarn add instantsearch.js
Then
const instantsearch = require('instantsearch.js');
Initialization
To initialize instant search you will need an Algolia account with a non-empty index. Provide app credentials then call the start
method.
Its advisable to use a separate javascript file ie
algolia.js
or any other name you like.
// algolia.js
const search = instantsearch({
appId: 'your_app_id',
apiKey: 'your_api_key',
indexName: 'index_name',
routing: true
});
search.start();
The appId
,apiKey
and indexName
are mandatory as gotten from Algolia Dashboard
The
apiKey
should be theSearch-Only API Key
. This key doesn’t have any write access, you should not worry about committing it in your version control. It gives public search access to a public website. You can always regenerate this key in your Algolia dashboard.
Congrats! you are now connected with Algolia.
Display results
The importance of search is to display results, by default InstantSearch.js will do a query at the start of the page and will retrieve the most relevant hits. To display results, hits widget will be used. Hits widget will display all the results returned by Algolia and update when new results are passed. With instantSearch.js you need to provide a container for each widget which tells instantSearch.js where to display the widget. Learn more about widgets. Here we first define the container of our results, this could be in any .html
file/page where we want to display results.
<!-- index.html -->
<div id="hits">
<!-- Hits widget will appear here -->
</div>
Once you set a container for the hits, add the hits widget in instantSearch instance, using addWidget
method. Add this in algolia.js
or the same javascript file you initialized Algolia.
// algolia.js
const search = instantsearch({
appId: 'your_app_id',
apiKey: 'your_api_key',
indexName: 'index_name',
routing: true
});
search.addWidget(
instantsearch.widgets.hits({
container: '#hits'
})
);
search.start();
You can now be able to see the results without styling. This view lets you inspect the values that are retrieved from Algolia, to build your custom view. To customize the view we need a special option for hits called template
, the option accepts a mustache template string or a function returning a string
.
const search = instantsearch({
appId: 'your_app_id',
apiKey: 'your_api_key',
indexName: 'index_name',
routing: true
});
search.addWidget(
instantsearch.widgets.hits({
container: '#hits',
templates: {
empty: 'No results',
item: '<em>Hit </em>: }'
}
})
);
search.start();
The above example used _highlightResult
that contains attributes highlighted based on the current query. This aspect of the search gives user feedback on the matching parts of the results.
We can also use a function returning a string, which I find better since here we can be able to pass actual HTML syntax for styling.
We used this approach in zegetech website since its more flexible and presentable
Example.
search.addWidget(
instantsearch.widgets.hits({
container: '#hits',
templates: {
empty: 'No results',
item: data => '
<div>
<h1 class="text-green">${data.title}</h1>
<p>${data.content.substring(0,150)}</p>
</div>
'
}
})
);
Note the arrow symbol represents ES6 syntax for defining a function
Using this approach:-
- You can be able to customize you view with html in a neat way.
- Use html classes from your favourite framework eg boostrap ie
<h1 class="text-green">${data.title}</h1>
- Attach valid javascript code in your results eg
data.content.substring(0,150)
to only display first 150 characters in a string.
Add search Box
Now that we have added results, we can start querying our index, to achieve this we need a searchBox widget.
In html index.html
<!--- index.html -->
<div id="search-box">
<!-- SearchBox widget will appear here -->
</div>
<div id="hits">
<!-- Hits widget will appear here -->
</div>
In javascript algolia.js
// algolia.js
const search = instantsearch({
appId: 'your_app_id',
apiKey: 'your_api_key',
indexName: 'index_name',
routing: true
});
// initialize SearchBox
search.addWidget(
instantsearch.widgets.searchBox({
container: '#search-box',
placeholder: 'Search for products'
})
);
// initialize hits widget
search.addWidget(
instantsearch.widgets.hits({
container: '#hits',
templates: {
empty: 'No results',
item: '<em>Hit </em>: }'
}
})
);
search.start();
The search is now active. The good thing Algolia computes the matching part. For more configuration results configure attributeToRetrieve and attributeToHighlight of your index.
We now can search our website and find those posts that we want, quickly and easily. Bravo!!
Limitations
Algolia community plan provides 50K operations and 10k records per month, pretty generous. Perfect for a small to medium website. To know more about how Algolia counts records and operations check their official blog. Apart from that below some of the limitations that the community plan lacks compared to an enterprise plan. they can be classified in terms of features and support. Check Algolia pricing page to see the difference of various plans.
Features | Support |
---|---|
Advanced analytics | Email support |
Advanced APIs(Analytics, Insights, and monitoring) | Extension support |
Personalization | Coding guidance |
Query Rules (Merchandising & Intent detection) | Live chat for implementation support |
Service level agreement (SLA) | Phone alerting |
Additional Team members | Dedicated point of contact |
Granular Team Permissions | Desiccated implementation engineer |
Give it a try and add some search to your static site.