Home Blog How to Convert an HTML Template to a Jekyll Theme
Tutorial

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.

How to Convert an HTML Template to a Jekyll Theme

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:

  1. The repeated wrapper — the <html>, <head>, <header>, <footer> that appears on every page → becomes _layouts/default.html
  2. Page-specific content — the main <main> content area → becomes {{ content }} in the layout
  3. Repeated components — navigation, sidebar, footer → become includes
  4. CSS files → move to _sass/ and convert to Sass (or keep as plain CSS in assets/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>&copy; 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_url filter for correct paths on project sites
  • Nav and footer → extracted to includes
  • Added `
How to Convert an HTML Template to a Jekyll Theme | JekyllHub

` 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>&copy; {{ '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.

Share LinkedIn