How Can You Use Ruby Hooks to Automate Content Processing in Jekyll
What Are Jekyll Hooks and Why Should You Use Them?
Jekyll Hooks are Ruby-based lifecycle callbacks that let you inject logic at key stages of your site’s build process. With hooks, you can:
- Modify post content before it renders
- Inject dynamic metadata into every page
- Clean or normalize Markdown before build
- Log and track build progress
They give you deep automation, while staying 100% static and Ruby-native.
Available Hook Points in Jekyll
| Entity | Hook Event |
|-----------|--------------------|
| Site | :after_init, :post_read, :post_write |
| Page/Post | :pre_render, :post_render, :post_write |
| Document | :pre_render, :post_render, :post_write |
Each hook gives you access to the relevant object (site, page, or document), and lets you modify it before or after Jekyll does its work.
Basic Syntax for Hook
Jekyll::Hooks.register ENTITY, EVENT do |item|
# do something with item
end
You can place this in any _plugins/*.rb file.
Example 1: Add Word Count to All Posts
Create file:_plugins/wordcount.rb
Jekyll::Hooks.register :posts, :post_render do |post|
wordcount = post.content.strip.split.size
post.data["word_count"] = wordcount
end
Use in layout:
Word count: {{ page.word_count }}
This adds a word count to every post automatically, without editing front matter.
Example 2: Auto-Generate Meta Description from First Paragraph
Jekyll::Hooks.register :posts, :pre_render do |post|
first_paragraph = post.content.match(/<p>(.*?)<\/p>/m)
if first_paragraph
clean_text = first_paragraph[1].gsub(/<.*?>/, '').strip
post.data["description"] ||= clean_text[0..160]
end
end
This automatically fills in a meta description if not manually provided.
Example 3: Log Every Page Render
Jekyll::Hooks.register :pages, :post_render do |page|
puts "Rendered page: #{page.path}"
end
Very useful for debugging large builds or CI pipelines.
Example 4: Clean Up Empty Tags Field
Jekyll::Hooks.register :documents, :post_read do |doc|
if doc.data["tags"].is_a?(Array) && doc.data["tags"].empty?
doc.data.delete("tags")
end
end
This prevents empty arrays from polluting Liquid loops or front matter dumps.
Can You Chain Hooks or Make Them Conditional?
Yes. You can stack multiple hooks for the same event, and inside the block you can check conditions based on file type, layout, or any front matter key.
Jekyll::Hooks.register :documents, :pre_render do |doc|
next unless doc.extname == ".md"
doc.content.gsub!(/TODO:/, '**Reminder:**')
end
Common Use Cases for Hooks
- Pre-processing Markdown content
- Setting default values
- Injecting canonical URLs
- Building custom permalinks
- Auto-tagging based on content
Where Do You Put Jekyll Hooks?
All hook code should go inside the _plugins directory. If it’s not being executed, check:
- Jekyll is not in
--safemode - You’re running the site with Bundler:
bundle exec jekyll build - Plugin files have
.rbextension and valid Ruby code
Compatibility: Can Hooks Be Used on GitHub Pages?
No — GitHub Pages disables custom plugins and hooks. You must use Netlify, Cloudflare Pages, or your own CI/CD workflow to build locally and deploy.
Conclusion
Jekyll hooks let you unlock full control over your content pipeline. With just a few lines of Ruby, you can automate tasks that would require plugins, JS, or manual editing in other platforms.
- Automate formatting, defaults, logging
- Preprocess or postprocess any content
- Clean your Markdown before render
Next Steps
- Experiment with
:pre_renderand:post_renderhooks - Log page builds to debug performance
- Inject custom variables like canonical URL, social image, or author info
In the next article, we’ll explore how to use Ruby in Jekyll to fetch remote content (like blog posts or GitHub issues) and render it statically during the build.