In the realm of technical documentation, content is king—but structure is the kingdom. A well-organized documentation site can make the difference between a frustrating user experience and an enlightening one. When users can easily find what they’re looking for, whether through intuitive navigation or efficient search, they’re more likely to engage with and benefit from your documentation.
Jekyll, with its flexibility and power, has long been a favorite tool for creating documentation sites. But when it comes to organizing complex documentation structures, especially those with multiple sections, nested categories, and interrelated content, even Jekyll can use a boost. This is where the Jekyll Collection Pages plugin shines, offering enhanced capabilities for structuring and organizing your content.
The foundation of a great documentation site is its structure. A well-planned structure makes navigation intuitive and helps users quickly find the information they need.
Before diving into Jekyll configurations, take some time to plan your content hierarchy. Consider the following:
For example, a software documentation site might have a structure like this:
- Getting Started
- Installation
- Quick Start Guide
- User Guide
- Basic Features
- Advanced Features
- API Reference
- Troubleshooting
- FAQs
With your content hierarchy in mind, it’s time to set up Jekyll collections. Collections in Jekyll allow you to group related content, which is perfect for documentation sites.
In your _config.yml
file, define your collections:
collections:
getting_started:
output: true
permalink: /docs/getting-started/:path/
user_guide:
output: true
permalink: /docs/user-guide/:path/
api_reference:
output: true
permalink: /docs/api/:path/
troubleshooting:
output: true
permalink: /docs/troubleshooting/:path/
Now, let’s configure the Jekyll Collection Pages plugin to work with our structure. Add this to your _config.yml
:
collection_pages:
- collection: getting_started
tag_field: category
path: docs/getting-started/categories
layout: category_page.html
- collection: user_guide
tag_field: category
path: docs/user-guide/categories
layout: category_page.html
- collection: api_reference
tag_field: category
path: docs/api/categories
layout: category_page.html
- collection: troubleshooting
tag_field: category
path: docs/troubleshooting/categories
layout: category_page.html
This configuration tells the plugin to create category pages for each of our documentation sections.
Let’s look at how to implement this structure. Create directories for each collection in your Jekyll project:
_getting_started/
_user_guide/
_api_reference/
_troubleshooting/
Then, create Markdown files in these directories. For example, in _getting_started/
:
---
title: Installation
category: Setup
---
# Installation Guide
Here's how to install our software...
The Jekyll Collection Pages plugin will automatically create a category page at /docs/getting-started/categories/setup.html
listing all documents in the “Setup” category.
Even with the best structure, users often rely on search to find specific information quickly. Let’s implement a search function using Lunr.js, a lightweight, full-text search library for client-side applications.
First, add Lunr.js to your project. You can do this by adding the following to your default layout’s <head>
section:
<script src="https://unpkg.com/lunr/lunr.js"></script>
Next, create a JSON file that Lunr.js will use as its search index. Add this to your _config.yml
:
plugins:
- jekyll-collection-pages
- jekyll-lunr-js-search
Create a search.json
file in your root directory:
---
layout: null
---
[
{% for collection in site.collections %}
{% for doc in collection.docs %}
{
"title": {{ doc.title | jsonify }},
"content": {{ doc.content | strip_html | jsonify }},
"url": {{ doc.url | jsonify }}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
{% endfor %}
]
Create a search page (search.html
in your root directory):
---
layout: default
title: Search
---
<h1>Search</h1>
<input type="text" id="search-input" placeholder="Search...">
<ul id="search-results"></ul>
<script>
window.store = {
{% for collection in site.collections %}
{% for doc in collection.docs %}
"{{ doc.url | slugify }}": {
"title": "{{ doc.title | xml_escape }}",
"content": {{ doc.content | strip_html | strip_newlines | jsonify }},
"url": "{{ doc.url | xml_escape }}"
}
{% unless forloop.last %},{% endunless %}
{% endfor %}
{% endfor %}
};
</script>
<script src="/path/to/lunr.min.js"></script>
<script src="/path/to/search.js"></script>
Create search.js
in your JavaScript directory:
(function() {
function displaySearchResults(results, store) {
var searchResults = document.getElementById('search-results');
if (results.length) {
var appendString = '';
for (var i = 0; i < results.length; i++) {
var item = store[results[i].ref];
appendString += '<li><a href="' + item.url + '"><h3>' + item.title + '</h3></a>';
appendString += '<p>' + item.content.substring(0, 150) + '...</p></li>';
}
searchResults.innerHTML = appendString;
} else {
searchResults.innerHTML = '<li>No results found</li>';
}
}
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split('&');
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('=');
if (pair[0] === variable) {
return decodeURIComponent(pair[1].replace(/\+/g, '%20'));
}
}
}
var searchTerm = getQueryVariable('query');
if (searchTerm) {
document.getElementById('search-box').setAttribute("value", searchTerm);
var idx = lunr(function () {
this.field('id');
this.field('title', { boost: 10 });
this.field('content');
});
for (var key in window.store) {
idx.add({
'id': key,
'title': window.store[key].title,
'content': window.store[key].content
});
}
var results = idx.search(searchTerm);
displaySearchResults(results, window.store);
}
})();
The Jekyll Collection Pages plugin really shines when it comes to organizing your content. Let’s explore some advanced uses.
We’ve already set up basic category pages. Let’s customize their layout. Create _layouts/category_page.html
:
---
layout: default
---
<h1>{{ page.tag }}</h1>
<ul>
{% for post in page.posts %}
<li><a href="{{ post.url | relative_url }}">{{ post.title }}</a></li>
{% endfor %}
</ul>
Use collection data to generate a dynamic sidebar. Add this to your default layout:
<nav class="sidebar">
{% for collection in site.collections %}
{% if collection.label != "posts" %}
<h3>{{ collection.label | capitalize | replace: "_", " " }}</h3>
<ul>
{% for doc in collection.docs %}
<li><a href="{{ doc.url | relative_url }}">{{ doc.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
{% endfor %}
</nav>
Add breadcrumbs to your document layout for easy navigation:
<div class="breadcrumbs">
<a href="{{ '/' | relative_url }}">Home</a> »
{% assign crumbs = page.url | split: '/' %}
{% for crumb in crumbs offset: 1 %}
{% if forloop.last %}
{{ page.title }}
{% else %}
<a href="{{ site.baseurl }}{% assign crumb_limit = forloop.index | plus: 1 %}{% for crumb in crumbs limit: crumb_limit %}{{ crumb | append: '/' }}{% endfor %}">{{ crumb | replace: '-', ' ' | capitalize }}</a> »
{% endif %}
{% endfor %}
</div>
Utilize tags for suggesting related documents. In your document layout:
{% if page.tags %}
<h3>Related Documents</h3>
<ul>
{% assign maxRelated = 4 %}
{% assign minCommonTags = 1 %}
{% assign maxRelatedCounter = 0 %}
{% for doc in site.documents %}
{% assign sameTagCount = 0 %}
{% for tag in doc.tags %}
{% if page.tags contains tag %}
{% assign sameTagCount = sameTagCount | plus: 1 %}
{% endif %}
{% endfor %}
{% if sameTagCount >= minCommonTags %}
<li><a href="{{ doc.url | relative_url }}">{{ doc.title }}</a></li>
{% assign maxRelatedCounter = maxRelatedCounter | plus: 1 %}
{% if maxRelatedCounter >= maxRelated %}
{% break %}
{% endif %}
{% endif %}
{% endfor %}
</ul>
{% endif %}
Consistent Front Matter: Establish a template for front matter across your documents. This ensures consistency and makes it easier to implement site-wide features.
Versioning Documentation: For software documentation, consider using Git branches for different versions. You can then build separate sites for each version.
Handling Images and Assets: Store images in an assets
folder within each collection. This keeps your content organized and makes it easy to move or refactor sections.
Let’s say we have an existing documentation site with a flat structure, where all documents are in the _posts
folder. Here’s how we might refactor it:
Before:
_posts/
2023-01-01-installation.md
2023-01-02-quick-start.md
2023-01-03-advanced-features.md
2023-01-04-api-reference.md
2023-01-05-troubleshooting.md
After:
_getting_started/
installation.md
quick-start.md
_user_guide/
advanced-features.md
_api_reference/
index.md
_troubleshooting/
common-issues.md
This refactoring, combined with the Jekyll Collection Pages plugin configuration we discussed earlier, results in:
By leveraging Jekyll’s collection feature and the power of the Jekyll Collection Pages plugin, you can create a documentation site that’s not only comprehensive but also user-friendly and easy to maintain. The key takeaways are:
With these strategies in place, your Jekyll documentation site will be well-structured, easily navigable, and primed for growth. Happy documenting!