Jekyll Variables Reference: site, page, layout, and More
A complete reference for every Jekyll variable — site.*, page.*, layout.*, content, forloop, and paginator — with examples for each.
Jekyll makes a set of variables available in every template through Liquid. Knowing which variables exist and what they contain is essential for building and customising Jekyll themes. This is a complete reference for all of them.
site variables
site contains global data about your Jekyll site — configuration from _config.yml, collections, posts, and build information.
Configuration variables
Every key in _config.yml becomes a site.* variable:
# _config.yml
title: "JekyllHub"
author: "Marcus Webb"
description: "A Jekyll theme marketplace."
url: "https://jekyllhub.com"
baseurl: ""
google_analytics: "G-XXXXXXXXXX"
sendy_list_id: "abc123"
JekyllHub → "JekyllHub"
The premier marketplace for Jekyll themes. Browse, preview, and download stunning themes for your next project. → "A Jekyll theme marketplace."
https://jekyllhub.com → "https://jekyllhub.com"
→ ""
{"name"=>"JekyllHub", "email"=>"hello@jekyllhub.com"} → "Marcus Webb"
G-W5Z2Z9GNPG→ "G-XXXXXXXXXX"
→ "abc123"
Built-in site variables
These are provided by Jekyll itself, not from _config.yml:
| Variable | Type | Description |
|---|---|---|
site.time |
DateTime | The time of the current build |
site.pages |
Array | All pages in the site |
site.posts |
Array | All posts, sorted newest first |
site.related_posts |
Array | Up to 10 related posts (for the current post) |
site.static_files |
Array | All static files (non-processed) |
site.html_pages |
Array | Pages with .html or .htm extension |
site.html_files |
Array | Static files with .html extension |
site.collections |
Array | All collections defined in config |
site.data |
Object | Data from all files in _data/ |
site.documents |
Array | All documents in all collections |
site.categories |
Object | Posts grouped by category |
site.tags |
Object | Posts grouped by tag |
site.posts
{% comment %} All posts, newest first {% endcomment %}
{% for post in site.posts %}
<a href="{{ post.url }}">{{ post.title }}</a>
{% endfor %}
{% comment %} Post count {% endcomment %}
{{ site.posts | size }} posts
{% comment %} Latest post {% endcomment %}
{% assign latest = site.posts | first %}
{{ latest.title }}
site.pages
{% for page in site.pages %}
{% if page.title %}
<a href="{{ page.url }}">{{ page.title }}</a>
{% endif %}
{% endfor %}
Note: site.pages includes all pages — HTML files, Markdown files, and some generated files. Filter by page.layout or page.url if you need a subset.
site.data
Mirrors the _data/ directory structure:
_data/
├── navigation.yml
├── authors.yml
└── showcase/
└── sites.yml
→ contents of navigation.yml
→ contents of authors.yml
→ contents of showcase/sites.yml
site.categories and site.tags
{% comment %} Loop over all categories {% endcomment %}
{% for category in site.categories %}
<h2>{{ category[0] }}</h2> ← category name
{% for post in category[1] %}
<li>{{ post.title }}</li> ← posts in this category
{% endfor %}
{% endfor %}
{% comment %} Posts in a specific category {% endcomment %}
{% assign tutorial_posts = site.categories["Tutorial"] %}
{{ tutorial_posts | size }} tutorials
Collection variables
For a collection named themes (defined in _config.yml):
{{ site.themes }} → array of all theme documents
{{ site.themes | size }} → number of themes
{% for theme in site.themes %}
{{ theme.title }}
{% endfor %}
page variables
page contains data about the current page, post, or collection document being rendered.
Built-in page variables
| Variable | Type | Description |
|---|---|---|
page.content |
String | Rendered HTML content of the page |
page.title |
String | Title from front matter |
page.excerpt |
String | Excerpt (first paragraph or custom) |
page.url |
String | URL of the page (e.g. /blog/my-post/) |
page.date |
DateTime | Post date |
page.id |
String | Unique identifier (e.g. /2026/08/06/my-post) |
page.categories |
Array | Categories from front matter |
page.tags |
Array | Tags from front matter |
page.path |
String | Source file path (e.g. _posts/2026-08-06-my-post.md) |
page.name |
String | Filename (e.g. 2026-08-06-my-post.md) |
page.next |
Object | Next post (chronologically) |
page.previous |
Object | Previous post (chronologically) |
Custom front matter variables
Every key in a page’s front matter becomes a page.* variable:
---
layout: post
title: "My Post"
author: Marcus Webb
featured: true
reading_time: 8
image: /assets/images/blog/cover.webp
difficulty: beginner
---
Marcus Webb → "Marcus Webb"
false → true
→ 8
/assets/images/blog/jekyll-variables-reference.webp → "/assets/images/blog/cover.webp"
→ "beginner"
page.url vs page.id
/tutorial/2026/06/28/jekyll-variables-reference/ → "/blog/jekyll-variables-reference/"
/tutorial/2026/06/28/jekyll-variables-reference → "/2026/08/06/jekyll-variables-reference"
page.url is the clean URL visitors see. page.id is an internal identifier used by some plugins.
page.date
2026-06-28 00:00:00 +0000 → 2026-08-06 00:00:00 +0000
June 28, 2026 → August 6, 2026
2026-06-28 → 2026-08-06
1782604800 → Unix timestamp
page.excerpt
<p>Jekyll makes a set of variables available in every template through Liquid. Knowing which variables exist and what they contain is essential for building and customising Jekyll themes. This is a complete reference for all of them.</p>
→ first paragraph (rendered HTML)
Jekyll makes a set of variables available in every template through Liquid. Knowing which variables exist and what they contain is essential for building and customising Jekyll themes. This is a complete reference for all of them.
→ plain text excerpt
Jekyll makes a set of variables available in every template through Liquid. Knowing which variables exist and what they contain is essential for building and customising Jekyll themes. This is...
Override the default excerpt in front matter:
excerpt: "A custom summary for this post."
page.next and page.previous
Navigate between posts:
{% if page.next %}
<a href="{{ page.next.url }}">Next: {{ page.next.title }}</a>
{% endif %}
{% if page.previous %}
<a href="{{ page.previous.url }}">Previous: {{ page.previous.title }}</a>
{% endif %}
Note: page.next is the chronologically newer post; page.previous is the older one — counterintuitive but correct.
page.categories and page.tags
{% for category in page.categories %}
<a href="/category/{{ category | slugify }}/">{{ category }}</a>
{% endfor %}
{% for tag in page.tags %}
<span class="tag">{{ tag }}</span>
{% endfor %}
layout variables
layout contains data from the current layout file’s front matter:
<!-- _layouts/post.html -->
---
layout: default
sidebar: true
show_related: true
---
→ true
→ true
Useful when a layout needs configuration that individual pages can check:
{% if layout.sidebar %}
{% include sidebar.html %}
{% endif %}
content
Available only inside layout files. Contains the rendered HTML content being wrapped by the layout:
<!-- _layouts/default.html -->
<main>
<div class="read-progress" id="read-progress"></div>
<article class="post" itemscope itemtype="https://schema.org/BlogPosting">
<header class="post-hero">
<div class="container">
<div class="post-hero__breadcrumb">
<a href="/">Home</a>
<span class="breadcrumb-sep">›</span>
<a href="/blog/">Blog</a>
<span class="breadcrumb-sep">›</span>
<span>What Makes a Good Jekyll Theme? (Checklist Before You Buy)</span>
</div>
<div class="post-hero__inner">
<span class="post-hero__cat">Themes</span>
<h1 class="post-hero__title" itemprop="name">What Makes a Good Jekyll Theme? (Checklist Before You Buy)</h1>
<p class="post-hero__desc" itemprop="description">Not all Jekyll themes are created equal. Here is what separates a well-built Jekyll theme from a pretty one — covering code quality, performance, SEO, accessibility, and long-term maintainability.</p>
<div class="post-hero__meta">
<span class="post-meta-item">
<svg width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg>
Updated <time itemprop="dateModified" datetime="2026-06-27T00:00:00+00:00">June 27, 2026</time>
</span>
<span class="post-meta-item">
<svg width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
9 min read
</span>
</div>
</div>
</div>
</header>
<div class="post-cover">
<div class="container">
<img src="/assets/images/blog/what-makes-a-good-jekyll-theme.webp" alt="What Makes a Good Jekyll Theme? (Checklist Before You Buy)" class="post-cover__img" itemprop="image">
</div>
</div>
<div class="container">
<div class="post-body">
<div class="post-body__main">
<div class="post-toc" id="post-toc" data-collapsed="false" style="display:none">
<button class="post-toc__label" id="toc-toggle" aria-expanded="false" aria-controls="toc-body">
<span class="post-toc__label-left">
<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h10"/></svg>
Table of Contents
</span>
<svg class="post-toc__chevron" width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg>
</button>
<nav id="toc-body" class="toc"></nav>
</div>
<div class="prose" itemprop="articleBody">
<p>A Jekyll theme can look polished in a screenshot and be a nightmare to work with in practice. Bad themes have hard-coded values scattered across templates, SCSS that cannot be overridden without editing source files, and JavaScript that blocks rendering on every page load.</p>
<p>A good Jekyll theme does the opposite: it is designed to be used, extended, and maintained — not just previewed. Here is what to look for before you commit.</p>
<h2 id="clean-readable-liquid-templates">Clean, Readable Liquid Templates</h2>
<p>Open the theme’s <code class="language-plaintext highlighter-rouge">_layouts/</code> and <code class="language-plaintext highlighter-rouge">_includes/</code> directories on GitHub and read a few files. Well-written Liquid templates are:</p>
<p><strong>Modular</strong> — the layout is broken into includes (<code class="language-plaintext highlighter-rouge">_includes/header.html</code>, <code class="language-plaintext highlighter-rouge">_includes/footer.html</code>, <code class="language-plaintext highlighter-rouge">_includes/head.html</code>) rather than crammed into one monolithic <code class="language-plaintext highlighter-rouge">default.html</code>. This makes it possible to override a single component without copying the entire layout.</p>
<p><strong>Commented where it matters</strong> — complex Liquid logic (conditional includes, data lookups, paginator handling) should have a brief comment explaining what it does. Templates without any comments are harder to customise correctly.</p>
<p><strong>Free of hard-coded content</strong> — good themes use <code class="language-plaintext highlighter-rouge">_config.yml</code> variables for site name, author, social links, and colour settings. A theme with your branding written directly into templates requires manual find-and-replace to customise.</p>
<p><strong>Consistent naming conventions</strong> — front matter fields should follow a pattern (<code class="language-plaintext highlighter-rouge">title</code>, <code class="language-plaintext highlighter-rouge">description</code>, <code class="language-plaintext highlighter-rouge">image</code>, <code class="language-plaintext highlighter-rouge">author</code>) rather than an idiosyncratic mix that requires reading every template to understand.</p>
<h2 id="well-organised-scss">Well-Organised SCSS</h2>
<p>SCSS structure reveals a lot about how seriously a theme was built. A good Jekyll theme organises styles using:</p>
<p><strong>Variables for everything visual</strong> — colours, font sizes, spacing, border radii, and breakpoints should all be defined as SCSS variables or CSS custom properties at the top of the stylesheet. This means you can retheme the entire site by changing a handful of values.</p>
<p><strong>Logical file structure</strong> — styles split into logical partials: <code class="language-plaintext highlighter-rouge">_variables.scss</code>, <code class="language-plaintext highlighter-rouge">_base.scss</code>, <code class="language-plaintext highlighter-rouge">_typography.scss</code>, <code class="language-plaintext highlighter-rouge">_layout.scss</code>, <code class="language-plaintext highlighter-rouge">_components.scss</code>. A single <code class="language-plaintext highlighter-rouge">style.scss</code> file with 3,000 lines of undifferentiated CSS is a red flag.</p>
<p><strong>No <code class="language-plaintext highlighter-rouge">!important</code> overuse</strong> — <code class="language-plaintext highlighter-rouge">!important</code> is sometimes necessary, but a theme that uses it everywhere is fighting its own specificity and will be painful to customise.</p>
<p><strong>Dark mode handled properly</strong> — if the theme supports dark mode, it should use CSS custom properties swapped via a <code class="language-plaintext highlighter-rouge">[data-theme="dark"]</code> attribute or <code class="language-plaintext highlighter-rouge">prefers-color-scheme</code> media query, not a separate stylesheet loaded with JavaScript.</p>
<h2 id="performance-by-default">Performance by Default</h2>
<p>A static site has no excuse to be slow. Before buying or installing a theme, check what it loads:</p>
<p><strong>Minimal JavaScript</strong> — a blog or portfolio theme should need almost no JavaScript. If a theme loads jQuery, multiple animation libraries, and a carousel script for a site that displays text and images, those are unnecessary dependencies you will carry forever.</p>
<p><strong>Self-hosted or system fonts</strong> — themes that load Google Fonts make an external request on every page load. A well-optimised theme either uses system fonts (<code class="language-plaintext highlighter-rouge">font-family: system-ui, sans-serif</code>) or bundles the fonts locally with <code class="language-plaintext highlighter-rouge">font-display: swap</code>.</p>
<p><strong>Optimised images in templates</strong> — check the image includes. Does the theme use <code class="language-plaintext highlighter-rouge">loading="lazy"</code> on below-the-fold images? Does it specify <code class="language-plaintext highlighter-rouge">width</code> and <code class="language-plaintext highlighter-rouge">height</code> attributes to prevent layout shift?</p>
<p><strong>No render-blocking resources</strong> — CSS should be in the <code class="language-plaintext highlighter-rouge"><head></code>, JavaScript should be deferred or at the end of <code class="language-plaintext highlighter-rouge"><body></code>. Check the source of the demo — a <code class="language-plaintext highlighter-rouge"><script></code> tag without <code class="language-plaintext highlighter-rouge">defer</code> or <code class="language-plaintext highlighter-rouge">async</code> in the <code class="language-plaintext highlighter-rouge"><head></code> will block every page from rendering.</p>
<p>Run Lighthouse on the live demo. A good Jekyll theme should score above 90 on Performance with no effort on your part — the theme itself should not be the bottleneck.</p>
<h2 id="seo-ready-out-of-the-box">SEO Ready Out of the Box</h2>
<p>A well-built Jekyll theme handles SEO fundamentals automatically:</p>
<p><strong><code class="language-plaintext highlighter-rouge">jekyll-seo-tag</code> integration</strong> — the theme should include <code class="language-plaintext highlighter-rouge">{% seo %}</code> in <code class="language-plaintext highlighter-rouge">_layouts/default.html</code> or equivalent. This handles title tags, meta descriptions, Open Graph, Twitter cards, and canonical URLs with a single include.</p>
<p><strong>Proper heading hierarchy</strong> — one <code class="language-plaintext highlighter-rouge"><h1></code> per page (the post or page title), with <code class="language-plaintext highlighter-rouge"><h2></code> through <code class="language-plaintext highlighter-rouge"><h4></code> used for content structure. A theme that wraps the site name in an <code class="language-plaintext highlighter-rouge"><h1></code> on every page is hurting your SEO on every post.</p>
<p><strong>RSS feed</strong> — the theme should include a <code class="language-plaintext highlighter-rouge"><link rel="alternate" type="application/rss+xml"></code> in the head, pointing to a feed generated by <code class="language-plaintext highlighter-rouge">jekyll-feed</code>.</p>
<p><strong>Structured data support</strong> — better themes include JSON-LD structured data for articles, breadcrumbs, or site links. This is not required, but it is a sign of a theme built with search visibility in mind.</p>
<p><strong>Clean URLs</strong> — the theme should not produce URLs with <code class="language-plaintext highlighter-rouge">.html</code> extensions or unnecessary parameters. Jekyll handles this with <code class="language-plaintext highlighter-rouge">permalink: pretty</code> in <code class="language-plaintext highlighter-rouge">_config.yml</code> — check that the theme’s config sets this or documents how to do it.</p>
<h2 id="accessibility-that-is-not-an-afterthought">Accessibility That Is Not an Afterthought</h2>
<p>Accessibility in a theme is not just about compliance — it is about code quality. Themes that are accessible are also better structured, more maintainable, and more usable for everyone.</p>
<p><strong>Semantic HTML</strong> — content should use <code class="language-plaintext highlighter-rouge"><article></code>, <code class="language-plaintext highlighter-rouge"><nav></code>, <code class="language-plaintext highlighter-rouge"><main></code>, <code class="language-plaintext highlighter-rouge"><aside></code>, and <code class="language-plaintext highlighter-rouge"><footer></code> appropriately. A theme built entirely with <code class="language-plaintext highlighter-rouge"><div></code> elements is not just inaccessible — it is lazy.</p>
<p><strong>Skip navigation link</strong> — a “Skip to content” link as the first focusable element lets keyboard users bypass the navigation on every page. It is one line of HTML and CSS. Themes without it have not thought about keyboard navigation.</p>
<p><strong>ARIA labels on interactive elements</strong> — icon-only buttons (like a dark mode toggle or search icon) need <code class="language-plaintext highlighter-rouge">aria-label</code> attributes to be usable by screen readers.</p>
<p><strong>Focus styles</strong> — pressing Tab through the live demo should show a visible focus ring on every interactive element. Themes that remove focus styles with <code class="language-plaintext highlighter-rouge">outline: none</code> fail basic keyboard accessibility.</p>
<p><strong>Colour contrast</strong> — body text on its background should meet WCAG AA (4.5:1 ratio minimum). Check this with the browser’s accessibility panel or a contrast checker.</p>
<h2 id="active-maintenance-and-a-clear-update-history">Active Maintenance and a Clear Update History</h2>
<p>Even the best theme becomes a liability if it is abandoned. Signs of a well-maintained theme:</p>
<p><strong>A changelog</strong> — a <code class="language-plaintext highlighter-rouge">CHANGELOG.md</code> or release notes on GitHub showing what changed in each version. This tells you the author thinks carefully about changes and communicates them clearly.</p>
<p><strong>Regular releases</strong> — at least a few updates in the past year. Check the releases page on GitHub, not just the commit history.</p>
<p><strong>Responsive issue handling</strong> — open the GitHub issues and look for how the author responds to bug reports. Are bugs acknowledged and fixed? Or are issues sitting unanswered for months?</p>
<p><strong>A versioning scheme</strong> — themes that use semantic versioning (<code class="language-plaintext highlighter-rouge">1.2.0</code>, <code class="language-plaintext highlighter-rouge">1.2.1</code>) signal that the author understands the difference between a breaking change and a patch.</p>
<h2 id="good-documentation">Good Documentation</h2>
<p>Documentation is a direct measure of how much the author cares about the people using their theme.</p>
<p>A well-documented Jekyll theme covers:</p>
<ul>
<li><strong>Quick start</strong> — how to get the theme running locally in five minutes</li>
<li><strong>Configuration reference</strong> — every <code class="language-plaintext highlighter-rouge">_config.yml</code> option explained with example values</li>
<li><strong>Front matter fields</strong> — what each layout accepts and what is required vs optional</li>
<li><strong>Customisation guide</strong> — how to override layouts, styles, and includes without editing theme source files</li>
<li><strong>Deployment notes</strong> — any hosting-specific configuration required</li>
<li><strong>Upgrade guide</strong> — what to do when a new version is released</li>
</ul>
<p>If the only documentation is a README with a screenshot and a “fork this repo” instruction, the author has not thought about the experience of people actually using the theme.</p>
<h2 id="the-quality-checklist">The Quality Checklist</h2>
<p>Use this before buying or installing any Jekyll theme:</p>
<p><strong>Code quality</strong></p>
<ul class="task-list">
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Templates are modular (split into <code class="language-plaintext highlighter-rouge">_includes/</code>)</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />No hard-coded content — everything comes from <code class="language-plaintext highlighter-rouge">_config.yml</code> or front matter</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />SCSS uses variables for colours, fonts, and spacing</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />No excessive <code class="language-plaintext highlighter-rouge">!important</code> usage</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Dark mode uses CSS custom properties, not a separate stylesheet</li>
</ul>
<p><strong>Performance</strong></p>
<ul class="task-list">
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Lighthouse performance score above 90 on the live demo</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />No unnecessary JavaScript libraries</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Fonts are self-hosted or use system fonts</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Images use <code class="language-plaintext highlighter-rouge">loading="lazy"</code> and have <code class="language-plaintext highlighter-rouge">width</code>/<code class="language-plaintext highlighter-rouge">height</code> attributes</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />No render-blocking scripts in <code class="language-plaintext highlighter-rouge"><head></code></li>
</ul>
<p><strong>SEO</strong></p>
<ul class="task-list">
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Includes <code class="language-plaintext highlighter-rouge">jekyll-seo-tag</code></li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />One <code class="language-plaintext highlighter-rouge"><h1></code> per page</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />RSS feed linked in <code class="language-plaintext highlighter-rouge"><head></code></li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Clean URLs (no <code class="language-plaintext highlighter-rouge">.html</code> extensions)</li>
</ul>
<p><strong>Accessibility</strong></p>
<ul class="task-list">
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Uses semantic HTML elements (<code class="language-plaintext highlighter-rouge"><article></code>, <code class="language-plaintext highlighter-rouge"><nav></code>, <code class="language-plaintext highlighter-rouge"><main></code>)</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Has a skip navigation link</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Icon buttons have <code class="language-plaintext highlighter-rouge">aria-label</code> attributes</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Focus styles are visible</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Body text passes WCAG AA contrast (4.5:1)</li>
</ul>
<p><strong>Maintenance</strong></p>
<ul class="task-list">
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Last commit within 12 months</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Has a changelog or release notes</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Issues are responded to within a reasonable time</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Compatible with Jekyll 4.x</li>
</ul>
<p><strong>Documentation</strong></p>
<ul class="task-list">
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Quick start guide works</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Configuration options are documented</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Front matter fields are documented</li>
<li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Customisation without editing source files is explained</li>
</ul>
<hr />
<p>A theme that scores well on all five areas — code, performance, SEO, accessibility, and maintenance — is genuinely worth using. Most themes will excel in one or two and be mediocre in others. The checklist helps you make that trade-off consciously rather than discovering it after you have built your site.</p>
<p>Ready to find a theme that meets this standard? Browse the <a href="/themes/">JekyllHub theme directory</a> — every theme is hand-reviewed for quality before listing.</p>
</div>
<div class="post-tags">
<span class="tag">jekyll themes</span>
<span class="tag">jekyll theme quality</span>
<span class="tag">best jekyll themes</span>
<span class="tag">premium jekyll themes</span>
<span class="tag">jekyll theme checklist</span>
</div>
<div class="post-share">
<span class="post-share__label">Share</span>
<a href="https://twitter.com/intent/tweet?url=https%3A%2F%2Fjekyllhub.com%2Fthemes%2F2026%2F06%2F27%2Fwhat-makes-a-good-jekyll-theme%2F&text=What+Makes+a+Good+Jekyll+Theme%3F+%28Checklist+Before+You+Buy%29" target="_blank" rel="noopener" class="post-share__btn post-share__btn--twitter">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.746l7.73-8.835L1.254 2.25H8.08l4.253 5.622zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
X / Twitter
</a>
<a href="https://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fjekyllhub.com%2Fthemes%2F2026%2F06%2F27%2Fwhat-makes-a-good-jekyll-theme%2F&title=What+Makes+a+Good+Jekyll+Theme%3F+%28Checklist+Before+You+Buy%29" target="_blank" rel="noopener" class="post-share__btn post-share__btn--linkedin">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
LinkedIn
</a>
<button class="post-share__btn post-share__btn--copy" onclick="JekyllHub.copyPostLink(this)">
<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/></svg>
<span>Copy Link</span>
</button>
</div>
<nav class="post-nav" aria-label="Post navigation">
<div class="post-nav__prev">
<a href="/tutorial/2026/06/26/jekyll-liquid-tags-reference/" class="post-nav__link">
<span class="post-nav__label">← Previous</span>
<span class="post-nav__title">Jekyll Liquid Tags: The Complete Reference Guide</span>
</a>
</div>
<div class="post-nav__center">
<a href="/blog/" class="btn btn--secondary btn--sm">All Posts</a>
</div>
<div class="post-nav__next">
<a href="/tutorial/2026/06/28/jekyll-variables-reference/" class="post-nav__link post-nav__link--next">
<span class="post-nav__label">Next →</span>
<span class="post-nav__title">Jekyll Variables Reference: site, page, layout, and More</span>
</a>
</div>
</nav>
</div>
<aside class="post-body__sidebar">
<div class="sidebar-card">
<h3 class="sidebar-card__title">Browse Themes</h3>
<ul class="post-sidebar__links">
<li><a href="/jekyll-academic-themes/">🎓 Academic Themes</a></li>
<li><a href="/jekyll-blog-themes/">✍️ Blog Themes</a></li>
<li><a href="/jekyll-business-themes/">💼 Business Themes</a></li>
<li><a href="/jekyll-documentation-themes/">📚 Documentation Themes</a></li>
<li><a href="/jekyll-e-commerce-themes/">🛒 E-commerce Themes</a></li>
<li><a href="/jekyll-landing-page-themes/">🚀 Landing Page Themes</a></li>
<li><a href="/jekyll-personal-themes/">👤 Personal Themes</a></li>
<li><a href="/jekyll-portfolio-themes/">🎨 Portfolio Themes</a></li>
<li><a href="/jekyll-resume-cv-themes/">📄 Resume/CV Themes</a></li>
<li><a href="/jekyll-github-pages-themes/"><svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" style="display:inline;vertical-align:middle;margin-right:4px"><path d="M12 0C5.374 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0 1 12 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z"/></svg>GitHub Pages Themes</a></li>
</ul>
<a href="/themes/" class="btn btn--primary btn--full" style="margin-top:var(--space-5)">Browse All Themes →</a>
</div>
<div class="sidebar-card" style="margin-top:var(--space-6)">
<h3 class="sidebar-card__title">Submit Your Theme</h3>
<p style="font-size:0.875rem;color:var(--text-3);line-height:1.6;margin-bottom:var(--space-4)">Built a Jekyll theme? Share it with thousands of developers.</p>
<a href="/submit/" class="btn btn--secondary btn--full">Submit a Theme →</a>
</div>
</aside>
</div>
</div>
<!-- Related Themes — rendered by JS from SITE_DATA, shuffled per page load -->
<section class="post-related-themes" style="display:none">
<div class="container">
<h2 class="post-related-themes__title">Themes You Might Like</h2>
<div class="themes-grid themes-grid--4" id="post-related-themes-grid"
data-tags="["jekyll themes","jekyll theme quality","best jekyll themes","premium jekyll themes","jekyll theme checklist"]"
data-related-category="Blog"></div>
</div>
</section>
</article>
<!-- Back to top -->
<button class="back-to-top" id="back-to-top" aria-label="Back to top" onclick="window.scrollTo({top:0,behavior:'smooth'})">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 15l7-7 7 7"/>
</svg>
</button>
<script src="/assets/js/post.js" defer></script>
</main>
content is the output after all inner layouts have been applied. For a post using layout: post (which itself uses layout: default), by the time default.html sees content, it already contains the post’s HTML wrapped in post.html’s structure.
paginator variables
Available on paginated pages when using jekyll-paginate or jekyll-paginate-v2:
| Variable | Description |
|---|---|
paginator.page |
Current page number |
paginator.per_page |
Posts per page |
paginator.posts |
Posts on the current page |
paginator.total_posts |
Total post count |
paginator.total_pages |
Total page count |
paginator.previous_page |
Previous page number (or nil) |
paginator.previous_page_path |
URL of previous page |
paginator.next_page |
Next page number (or nil) |
paginator.next_page_path |
URL of next page |
{% for post in paginator.posts %}
<article>
<h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
</article>
{% endfor %}
<nav class="pagination">
{% if paginator.previous_page %}
<a href="{{ paginator.previous_page_path }}">← Newer</a>
{% endif %}
<span>Page {{ paginator.page }} of {{ paginator.total_pages }}</span>
{% if paginator.next_page %}
<a href="{{ paginator.next_page_path }}">Older →</a>
{% endif %}
</nav>
jekyll variables
Information about the Jekyll build environment:
production → "development" or "production"
3.10.0 → "4.3.2"
{% if jekyll.environment == "production" %}
{% include analytics.html %}
{% endif %}
Set JEKYLL_ENV=production before building to enable production-only features.
forloop variables
Inside any {% for %} loop:
{% for post in site.posts %}
{{ forloop.index }} → 1, 2, 3... (1-based)
{{ forloop.index0 }} → 0, 1, 2... (0-based)
{{ forloop.rindex }} → counts down from total (1-based)
{{ forloop.rindex0 }} → counts down from total (0-based)
{{ forloop.first }} → true on first iteration
{{ forloop.last }} → true on last iteration
{{ forloop.length }} → total items in array
{% endfor %}
Practical use — add a class to the first item and a divider between items:
{% for item in list %}
<div class="item{% if forloop.first %} item--first{% endif %}">
{{ item.name }}
</div>
{% unless forloop.last %}<hr>{% endunless %}
{% endfor %}
tablerow variables
Inside a {% tablerow %} loop:
{% tablerow item in list cols: 3 %}
{{ tablerowloop.index }}
{{ tablerowloop.col }} → current column (1-based)
{{ tablerowloop.col0 }} → current column (0-based)
{{ tablerowloop.col_first }} → true on first column
{{ tablerowloop.col_last }} → true on last column
{{ tablerowloop.row }} → current row number
{{ tablerowloop.first }} → true on first item
{{ tablerowloop.last }} → true on last item
{{ tablerowloop.length }} → total items
{% endtablerow %}
Quick reference: which variable to use
| You need | Use |
|---|---|
| Site name/URL from config | site.title, site.url |
| All blog posts | site.posts |
| All pages | site.pages |
Data from _data/nav.yml |
site.data.nav |
| Custom config value | site.your_key |
| Current page title | page.title |
| Current page URL | page.url |
| Custom front matter value | page.your_key |
| Post publish date | page.date |
| Post excerpt | page.excerpt |
| Next/previous post | page.next, page.previous |
| Rendered page content (in layouts) | content |
| Build environment | jekyll.environment |
| Pagination data | paginator.* |
| Loop position | forloop.index, forloop.first, etc. |