Jekyll Static Site Development
Jekyll is a static site generator written in Ruby, created by Tom Preston-Werner from GitHub. It is the first of the popular SSGs around which an ecosystem has formed. Direct integration with GitHub Pages makes it the standard for open-source documentation, personal blogs, and small corporate websites without complex infrastructure.
Project Architecture
mysite/
├── _config.yml # Main configuration
├── _config.dev.yml # Override for development
├── _data/ # YAML/JSON/CSV data
│ ├── navigation.yml
│ └── team.yml
├── _drafts/ # Post drafts
├── _includes/ # Reusable fragments (analogue of partial)
│ ├── head.html
│ ├── header.html
│ └── footer.html
├── _layouts/ # Base templates
│ ├── default.html
│ ├── post.html
│ └── page.html
├── _posts/ # Blog posts (YYYY-MM-DD-slug.md)
├── _sass/ # SCSS files
├── _site/ # Compiled output (gitignore)
├── assets/
│ ├── css/main.scss
│ └── js/
├── collections/ # Custom collections
└── index.md
_config.yml Configuration
title: "Site Title"
description: "Description for SEO"
url: "https://example.com"
baseurl: "" # Empty string for root domain, "/subdir" for subdirectory
author:
name: "Team"
email: "[email protected]"
# Permalink structure
permalink: /blog/:year/:month/:slug/
# Markdown processor
markdown: kramdown
kramdown:
input: GFM
hard_wrap: false
syntax_highlighter: rouge
syntax_highlighter_opts:
block:
line_numbers: true
# SASS
sass:
style: compressed
sass_dir: _sass
# Plugins
plugins:
- jekyll-feed
- jekyll-sitemap
- jekyll-seo-tag
- jekyll-paginate-v2
- jekyll-redirect-from
# Exclude from build
exclude:
- Gemfile
- Gemfile.lock
- node_modules
- vendor
- README.md
- "*.sh"
# Custom collections
collections:
services:
output: true
permalink: /services/:slug/
team:
output: true
permalink: /team/:slug/
# Default values for front matter
defaults:
- scope:
path: "_posts"
type: "posts"
values:
layout: "post"
author: "default"
comments: true
- scope:
path: "_services"
type: "services"
values:
layout: "service"
Liquid Templates
Jekyll uses Liquid — a templating language from Shopify. Syntax is simpler than Go Templates:
<!-- _layouts/default.html -->
<!DOCTYPE html>
<html lang="{{ page.lang | default: site.lang | default: 'en' }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
{% if page.title %}{{ page.title }} | {{ site.title }}{% else %}{{ site.title }}{% endif %}
</title>
{% seo %}
<link rel="stylesheet" href="{{ '/assets/css/main.css' | relative_url }}">
</head>
<body>
{% include header.html %}
<main>
{{ content }}
</main>
{% include footer.html %}
<script src="{{ '/assets/js/main.js' | relative_url }}" defer></script>
</body>
</html>
<!-- _layouts/post.html -->
---
layout: default
---
<article class="post">
<header class="post__header">
<h1>{{ page.title }}</h1>
<time datetime="{{ page.date | date_to_xmlschema }}">
{{ page.date | date: "%d %B %Y" }}
</time>
{% if page.tags %}
<ul class="tags">
{% for tag in page.tags %}
<li><a href="/tags/{{ tag | slugify }}/">{{ tag }}</a></li>
{% endfor %}
</ul>
{% endif %}
</header>
{% if page.image %}
<img src="{{ page.image | relative_url }}" alt="{{ page.title }}" loading="eager">
{% endif %}
<div class="post__content">{{ content }}</div>
{% if page.related_posts %}
{% include related-posts.html posts=page.related_posts %}
{% endif %}
</article>
Working with Data
# _data/services.yml
- title: "Web Development"
slug: "web-development"
icon: "code"
description: "Corporate sites, landing pages, e-commerce"
features:
- "Responsive layout"
- "SEO optimization"
- "CRM integration"
<!-- _includes/services-grid.html -->
<div class="services-grid">
{% for service in site.data.services %}
<div class="service-card">
<div class="service-card__icon">{{ service.icon }}</div>
<h3>{{ service.title }}</h3>
<p>{{ service.description }}</p>
<ul>
{% for feature in service.features %}
<li>{{ feature }}</li>
{% endfor %}
</ul>
<a href="/services/{{ service.slug }}/">Learn more</a>
</div>
{% endfor %}
</div>
Pagination via jekyll-paginate-v2
# _config.yml
pagination:
enabled: true
per_page: 12
permalink: '/page/:num/'
title: ':title - page :num'
sort_field: 'date'
sort_reverse: true
<!-- blog/index.html -->
---
layout: default
title: Blog
pagination:
enabled: true
collection: posts
category: news
---
<div class="posts-grid">
{% for post in paginator.posts %}
{% include post-card.html post=post %}
{% endfor %}
</div>
{% if paginator.total_pages > 1 %}
<nav class="pagination">
{% if paginator.previous_page %}
<a href="{{ paginator.previous_page_path | relative_url }}">← Previous</a>
{% endif %}
<span>{{ paginator.page }} of {{ paginator.total_pages }}</span>
{% if paginator.next_page %}
<a href="{{ paginator.next_page_path | relative_url }}">Next →</a>
{% endif %}
</nav>
{% endif %}
SCSS and Assets
Jekyll processes SCSS natively via _sass/. The file assets/css/main.scss must start with two lines of front matter:
---
---
@import "variables";
@import "base";
@import "components/header";
@import "components/footer";
@import "components/post-card";
@import "pages/home";
@import "pages/blog";
GitHub Actions for Deployment
# .github/workflows/jekyll.yml
name: Build and Deploy Jekyll
on:
push:
branches: [main]
jobs:
build-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
- name: Build
env:
JEKYLL_ENV: production
run: bundle exec jekyll build --config _config.yml
- name: Deploy to S3
run: aws s3 sync _site/ s3://${{ secrets.S3_BUCKET }} --delete
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Timeline
Site on a ready-made theme with custom content and basic setup — 3–5 days. Development from scratch: layouts, SCSS, collections, data, pagination, CI/CD — 2–3 weeks. Large site with multiple collections, multilingual support (jekyll-multiple-languages-plugin) and complex templates — 1–2 months.







