How do you structure Jekyll repos when using custom themes or branches
Jekyll allows themes to be installed as Ruby gems or included manually within your repository. While this flexibility is powerful, it also introduces complexity. When you choose to override theme components, or manage multiple layout variants, poor structure can lead to file conflicts, update difficulties, and broken layouts.
Without planning, your theme overrides may be scattered across the repo. This creates confusion—especially when returning months later to troubleshoot a broken layout or style. A scalable repo anticipates and isolates theme logic from custom site content and layouts.
What are the main approaches to using Jekyll themes?
There are three main ways to integrate themes:
- Gem-based themes: Installed via the
_config.ymlsetting and Gemfile. - Remote theme: Pulled from a GitHub repo using
remote_theme. - Forked or bundled themes: Manually cloned into the repo to allow full customization.
Each approach requires different structural adjustments. Gem-based themes encourage minimal overrides. Forked themes demand a structure that preserves original files while tracking your changes.
How do you organize overrides when using gem-based or remote themes?
Jekyll allows selective overriding of layouts, includes, Sass, and assets. Overrides should follow the same folder names as the original theme:
/
├── _includes/
├── _layouts/
├── _sass/
├── assets/
For example, if the theme uses a layout called default.html, place your customized version in _layouts/default.html. Jekyll will prioritize your override over the gem version. This strategy keeps your custom logic scoped and prevents accidental theme updates from overwriting changes.
How can you maintain the original theme alongside your custom structure?
When using a forked theme (copied into your repo), preserve the theme in its own folder for clarity:
theme/
├── _layouts/
├── _includes/
├── assets/
Then use symbolic includes or partial reuses to extend or override only parts needed. Your site-level folders remain in root, and you maintain a clear boundary between vendor theme logic and your site's customization.
How do GitHub Pages branches affect repo structure decisions?
GitHub Pages can deploy from:
mainormasterbranch (root or/docs)gh-pagesbranch (fully built site output)
Each option leads to a different project organization:
Option 1: Use /docs for site content
This works well for projects that need to host documentation alongside source code. Your layout looks like this:
/
├── docs/
│ ├── _config.yml
│ ├── _posts/
│ └── index.md
├── src/
├── README.md
In GitHub Pages settings, select /docs as the source. This keeps documentation contained and doesn’t interfere with development branches.
Option 2: Deploy from gh-pages branch
This method is suitable for separating source and output entirely. It’s popular in CI/CD workflows where Jekyll is built on the main branch, and the compiled HTML is pushed to gh-pages.
main/
├── _posts/
├── _layouts/
├── _config.yml
gh-pages/
├── index.html
├── assets/
├── sitemap.xml
This requires workflow automation (GitHub Actions or similar) to build and deploy Jekyll automatically to gh-pages.
What GitHub Actions setup works with this structure?
A basic CI/CD setup for Jekyll might look like:
name: Build and Deploy Jekyll
on:
push:
branches:
- main
jobs:
build-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-ruby@v1
with:
ruby-version: '3.2'
- run: gem install bundler
- run: bundle install
- run: bundle exec jekyll build
- uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./_site
This automatically deploys your built site from main to gh-pages on every push.
Should I use submodules for themes or content?
Submodules are useful if your team maintains shared themes across many sites. For example:
themes/
└── minimal-theme/ (Git submodule)
_config.yml:
theme: themes/minimal-theme
This enables you to update the theme across sites by pulling changes from the submodule. However, it adds Git complexity, so document clearly how to clone and initialize the repo correctly.
How do I structure version-controlled custom themes?
If you maintain your own custom theme, keep it in a separate repo. Then reference it via Git or gemspec:
remote_theme: your-org/your-theme
Or for local development:
theme: your-theme
gem "your-theme", path: "./themes/your-theme"
Within your main site, override only what you need using the normal Jekyll override pattern.
How can structure help manage multiple site variants?
If you need to maintain multiple versions of the same site (e.g., dev, staging, production), use either:
- Separate branches:
dev,main,prod - Config layering with environment-specific
_config.dev.yml,_config.prod.yml
Combine them during build:
bundle exec jekyll build --config _config.yml,_config.prod.yml
This strategy keeps your core structure intact while customizing for each environment.
Conclusion: How do themes and branches shape repo structure in Jekyll?
When working with themes or deployment branches, your Jekyll repo must accommodate overrides, configuration variants, and separation of content and output. A scalable structure keeps themes modular, documents all overrides, isolates environments via branches or config layering, and uses automation to reduce error.
Whether you're customizing a theme or running CI pipelines to gh-pages, structure is what ensures your project remains maintainable. Think modularly, version clearly, and document thoroughly. Your future self—and your team—will thank you.