How to Convert an HTML Template to a Jekyll Theme
A step-by-step guide to converting a static HTML template into a reusable Jekyll theme — with layouts, includes, front matter, and Sass integration.
You have an HTML template you like — clean design, good structure — but you want to run it as a Jekyll site so you can write in Markdown and deploy to GitHub Pages. Converting it is simpler than it sounds. This guide walks through the full process.
Step 1: Understand Jekyll’s File Structure
Before converting, know what Jekyll expects:
my-jekyll-theme/
├── _config.yml # Site configuration
├── _layouts/ # Page templates (wraps content)
├── _includes/ # Reusable HTML fragments
├── _sass/ # Sass partials
├── assets/
│ ├── css/ # Compiled CSS (or main.scss)
│ ├── js/
│ └── images/
├── _posts/ # Blog posts (dated Markdown files)
├── _pages/ # Static pages (optional)
└── index.md # Homepage (or index.html)
Step 2: Analyse Your HTML Template
Look at your HTML template and identify:
- The repeated wrapper — the
<html>,<head>,<header>,<footer>that appears on every page → becomes_layouts/default.html - Page-specific content — the main
<main>content area → becomes{{ content }}in the layout - Repeated components — navigation, sidebar, footer → become includes
- CSS files → move to
_sass/and convert to Sass (or keep as plain CSS inassets/css/)
Step 3: Create the Default Layout
Take your index.html and extract everything except the page-specific content. This becomes _layouts/default.html:
Before (index.html):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Site</title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about.html">About</a>
<a href="/blog.html">Blog</a>
</nav>
<main>
<h1>Welcome to My Site</h1>
<p>This is the homepage content...</p>
</main>
<footer>
<p>© 2026 My Site</p>
</footer>
<script src="assets/js/main.js"></script>
</body>
</html>
After (_layouts/default.html):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% if page.title %}{{ page.title }} | {% endif %}{{ site.title }}</title>
<meta name="description" content="{{ page.description | default: site.description }}">
{% seo %}
<link rel="stylesheet" href="{{ '/assets/css/main.css' | relative_url }}">
</head>
<body>
{% include header.html %}
<main class="main-content">
{{ content }}
</main>
{% include footer.html %}
<script src="{{ '/assets/js/main.js' | relative_url }}" defer></script>
</body>
</html>
Key changes:
- Hard-coded title →
{{ page.title }} | {{ site.title }} - Hard-coded description →
{{ page.description | default: site.description }} - Asset paths →
relative_urlfilter for correct paths on project sites - Nav and footer → extracted to includes
- Added `
` for SEO tags
Step 4: Create Includes
Extract the navigation into _includes/header.html:
<header class="site-header">
<div class="container">
<a href="{{ '/' | relative_url }}" class="site-logo">
{{ site.title }}
</a>
<nav class="site-nav">
<a href="{{ '/' | relative_url }}"
{% if page.url == '/' %}class="active"{% endif %}>Home</a>
<a href="{{ '/about/' | relative_url }}"
{% if page.url contains '/about' %}class="active"{% endif %}>About</a>
<a href="{{ '/blog/' | relative_url }}"
{% if page.url contains '/blog' %}class="active"{% endif %}>Blog</a>
</nav>
</div>
</header>
Extract the footer into _includes/footer.html:
<footer class="site-footer">
<div class="container">
<p>© {{ 'now' | date: "%Y" }} {{ site.title }}.
Built with <a href="https://jekyllrb.com">Jekyll</a>.</p>
</div>
</footer>
Step 5: Convert CSS to Sass
Move your CSS to _sass/ and create a main entry point at assets/css/main.scss:
---
---
// assets/css/main.scss
// Front matter (two triple-dashes) tells Jekyll to process this file
@import "variables";
@import "base";
@import "layout";
@import "components";
@import "typography";
Split your CSS into logical partials in _sass/:
_variables.scss— colours, fonts, spacing variables_base.scss— reset and base element styles_layout.scss— grid, container, page structure_components.scss— buttons, cards, forms, tags_typography.scss— headings, body text, links
Add to _config.yml:
sass:
sass_dir: _sass
style: compressed
Step 6: Create the Homepage
Create index.md (or index.html):
---
layout: home
title: Home
---
Create _layouts/home.html for the homepage-specific content:
---
layout: default
---
<section class="hero">
<h1>{{ page.hero_title | default: site.title }}</h1>
<p>{{ page.hero_subtitle | default: site.description }}</p>
<a href="{{ page.hero_cta_url | default: '/themes/' }}" class="btn">
{{ page.hero_cta_text | default: "Browse Themes" }}
</a>
</section>
{{ content }}
{% if site.posts.size > 0 %}
<section class="recent-posts">
<h2>Recent Posts</h2>
{% for post in site.posts limit: 3 %}
<article class="post-card">
<a href="{{ post.url }}"><h3>{{ post.title }}</h3></a>
<time>{{ post.date | date: "%B %d, %Y" }}</time>
</article>
{% endfor %}
</section>
{% endif %}
Step 7: Create the Post Layout
For blog posts, create _layouts/post.html:
---
layout: default
---
<article class="post">
<header class="post-header">
<h1>{{ page.title }}</h1>
<div class="post-meta">
<time datetime="{{ page.date | date_to_xmlschema }}">
{{ page.date | date: "%B %d, %Y" }}
</time>
{% if page.author %}
<span class="post-author">by {{ page.author }}</span>
{% endif %}
</div>
{% if page.image %}
<img src="{{ page.image | relative_url }}" alt="{{ page.title }}" class="post-image">
{% endif %}
</header>
<div class="post-content">
{{ content }}
</div>
<footer class="post-footer">
{% if page.tags.size > 0 %}
<div class="post-tags">
{% for tag in page.tags %}
<a href="/tag/{{ tag | downcase }}/" class="tag">{{ tag }}</a>
{% endfor %}
</div>
{% endif %}
</footer>
</article>
Step 8: Update Asset Paths
All asset paths in your templates need to use Jekyll’s relative_url or absolute_url filters:
<!-- Before -->
<img src="assets/images/logo.png" alt="Logo">
<script src="assets/js/app.js"></script>
<!-- After -->
<img src="{{ '/assets/images/logo.png' | relative_url }}" alt="Logo">
<script src="{{ '/assets/js/app.js' | relative_url }}" defer></script>
This ensures paths work correctly on both root-level sites (/) and project sites (/my-project/).
Step 9: Set Up _config.yml
title: "My Site"
description: "A description of my Jekyll site."
url: "https://yourdomain.com"
baseurl: ""
author: "Your Name"
# Build settings
markdown: kramdown
highlighter: rouge
# Plugins
plugins:
- jekyll-seo-tag
- jekyll-sitemap
# Sass
sass:
style: compressed
Step 10: Test Locally
bundle exec jekyll serve --livereload
Open http://localhost:4000 and check:
- Layout renders correctly on all page types
- CSS is loading (check browser dev tools Network tab)
- Images load with correct paths
- Navigation links work
- Posts render with correct front matter
Once your conversion is complete, you have a fully functional Jekyll theme you can reuse across multiple sites — or share it on a marketplace like JekyllHub.