What's on this page
Add client-side search
A static site has no server-side search, but Cecil can generate a JSON index of your pages at build time. A few lines of JavaScript are then enough to search it in the browser.
Create the index page
Create a page whose only output format is json, with a dedicated layout:
pages/search.md
---
title: Search index
layout: search
output: json
excluded: true
---
excluded: true keeps this technical page out of list pages and the sitemap.
Write the JSON template
Following the lookup rules, Cecil renders this page with layouts/search.json.twig. Build an array of the showable pages, then encode it:
{%- set index = [] -%}
{%- for p in site.pages.showable|filter(p => p.type == 'page') -%}
{%- set index = index|merge([{
title: p.title,
url: url(p),
description: p.description|default(''),
content: p.content|striptags|excerpt(300)
}]) -%}
{%- endfor -%}
{{ index|json_encode(constant('JSON_UNESCAPED_UNICODE') b-or constant('JSON_UNESCAPED_SLASHES'))|raw }}
After php cecil.phar build, the index is available at /search.json.
Search with Fuse.js
Add a search field to a template and load Fuse.js from a CDN to query the index:
<input type="search" id="search" placeholder="Search…">
<ul id="results"></ul>
<script type="module">
import Fuse from 'https://cdn.jsdelivr.net/npm/fuse.js@7/dist/fuse.mjs';
const index = await fetch('{{ url('search', {format: 'json'}) }}').then(r => r.json());
const fuse = new Fuse(index, { keys: ['title', 'description', 'content'], threshold: 0.3 });
const results = document.getElementById('results');
document.getElementById('search').addEventListener('input', (e) => {
results.replaceChildren(...fuse.search(e.target.value, { limit: 10 }).map(({ item }) => {
const li = document.createElement('li');
const a = document.createElement('a');
a.href = item.url;
a.textContent = item.title;
li.append(a);
return li;
}));
});
</script>
Alternative: Pagefind
Pagefind indexes the generated HTML files instead: no JSON template is needed. Run it after the build, on the output directory (_site by default):
php cecil.phar build
npx pagefind --site _site
Then load its UI in a template:
<link href="/pagefind/pagefind-ui.css" rel="stylesheet">
<script src="/pagefind/pagefind-ui.js"></script>
<div id="search"></div>
<script>
window.addEventListener('DOMContentLoaded', () => new PagefindUI({ element: '#search' }));
</script>