“Hexo is not just a static site generator; it’s a framework that empowers you to create, customize, and deploy beautiful blogs with minimal friction. Understanding its structure is the key to unlocking its full potential.”
📋 Table of Contents
Introduction to Hexo
The Root Directory Structure
Deep Dive: Source Directory
Understanding Themes
Configuration Files Explained
The Magic of Layouts
Working with Partials
Assets and Static Files
Advanced Customization
Deployment Strategies
Best Practices and Tips
Troubleshooting Common Issues
🌟 Introduction to Hexo Hexo is a fast, simple, and powerful blog framework powered by Node.js. It transforms your plain text files (written in Markdown or other markup languages) into static HTML files that can be deployed anywhere. But what makes Hexo truly special is its elegant architecture and extensible design.
Why Understanding Structure Matters When I first started with Hexo, I treated it like a black box—write posts, run hexo generate
, and deploy. But as my blog grew, I needed to customize layouts, add new features, and optimize performance. That’s when I realized that understanding Hexo’s structure wasn’t just helpful—it was essential.
💡 Pro Tip : Think of Hexo as a well-organized house. Each directory has a specific purpose, and knowing where everything is located makes maintenance and customization much easier.
📁 The Root Directory Structure Let’s start by examining the root directory of your Hexo blog. Here’s what you’ll typically see:
1 2 3 4 5 6 7 8 9 your-hexo-blog/ ├── _config.yml # Main configuration file ├── package.json # Node.js dependencies ├── scaffolds/ # Post templates ├── source/ # Your content ├── themes/ # Theme files ├── public/ # Generated static files (auto-created) ├── .deploy_git/ # Deployment files (auto-created) └── node_modules/ # Node.js packages (auto-created)
🔍 Key Files and Directories _config.yml
- The Heart of Your BlogThis is the main configuration file where you define:
Site metadata (title, description, author)
URL structure and permalinks
Directory settings
Writing preferences
Extension configurations
Deployment settings
Example Configuration:
1 2 3 4 5 6 7 8 9 10 title: My Awesome Blog subtitle: 'A journey through code and creativity' description: 'Welcome to my corner of the internet' author: Your Name language: entimezone: 'America/New_York' url: https://yourblog.com permalink: :year/:month/:day/:title/
package.json
- Dependencies and ScriptsThis file defines your project’s dependencies and includes useful scripts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 { "name" : "your-hexo-blog" , "version" : "1.0.0" , "scripts" : { "build" : "hexo generate" , "clean" : "hexo clean" , "deploy" : "hexo deploy" , "server" : "hexo server" }, "dependencies" : { "hexo" : "^6.3.0" , "hexo-renderer-marked" : "^6.0.0" , "hexo-server" : "^3.0.0" } }
📝 Deep Dive: Source Directory The source/
directory is where your content lives. This is the most important directory for day-to-day blogging.
1 2 3 4 5 6 7 8 9 source/ ├── _posts/ # Your blog posts ├── about/ # Custom pages ├── categories/ # Category pages ├── tags/ # Tag pages ├── images/ # Images and assets ├── css/ # Custom CSS files ├── js/ # Custom JavaScript files └── index.md # Homepage content
📄 Blog Posts (_posts/
) This is where you’ll spend most of your time. Each post is a Markdown file with front matter:
1 2 3 4 5 6 7 8 9 --- title: "Understanding Hexo Structure" date: 2025-08-07 15:30:00 tags: [hexo, blogging, web-development] categories: [tutorials] cover: /images/hexo-structure.jpg --- # Your content goes here...
Front Matter Fields:
title
: Post title
date
: Publication date
tags
: Array of tags
categories
: Array of categories
cover
: Featured image
layout
: Custom layout (optional)
comments
: Enable/disable comments (optional)
🗂️ Organizing Posts You can organize posts in subdirectories:
1 2 3 4 5 6 7 8 9 _posts/ ├── tutorials/ │ ├── hexo-basics.md │ └── advanced-hexo.md ├── reviews/ │ ├── book-review.md │ └── movie-review.md └── personal/ └── my-journey.md
📑 Custom Pages Create custom pages by adding directories and index.md
files:
1 2 3 4 5 6 7 source/ ├── about/ │ └── index.md ├── projects/ │ └── index.md └── contact/ └── index.md
Example about/index.md
:
1 2 3 4 5 6 7 8 9 --- title: About Me layout: page date: 2025-08-07 16:00:00 --- # About Me Welcome to my personal space! I'm a passionate developer...
🎨 Understanding Themes Themes control the visual appearance of your blog. Hexo comes with a default theme, but you can install and customize themes extensively.
1 2 3 4 5 6 7 themes/ └── your-theme/ ├── _config.yml # Theme-specific configuration ├── languages/ # Translation files ├── layout/ # Template files ├── source/ # Theme assets └── scripts/ # Theme scripts
🎭 Theme Structure Deep Dive Layout Directory 1 2 3 4 5 6 7 8 themes/your-theme/layout/ ├── _partial/ # Reusable template components ├── archive.ejs # Archive page template ├── category.ejs # Category page template ├── index.ejs # Homepage template ├── layout.ejs # Main layout template ├── page.ejs # Page template └── post.ejs # Post template
Source Directory 1 2 3 4 5 6 themes/your-theme/source/ ├── css/ # CSS files ├── fonts/ # Font files ├── images/ # Image assets ├── js/ # JavaScript files └── lib/ # Third-party libraries
🛠️ Installing and Customizing Themes
Install a theme:
1 npm install hexo-theme-cactus
Enable it in _config.yml
:
Customize theme settings:
1 2 3 4 5 6 7 8 9 10 nav: home: / about: /about/ archive: /archives/ categories: /categories/ tags: /tags/ rss: true favicon: /favicon.ico
⚙️ Configuration Files Explained Main Configuration (_config.yml
) Site Settings 1 2 3 4 5 6 7 title: toki pona today! subtitle: 'tenpo suno la o toki!' description: 'Welcome! **toki pona today** is a method to improve your comprehension...' keywords: author: jan Pitaki language: entimezone: 'America/New_York'
URL Configuration 1 2 3 4 5 6 7 url: http://tokipona.today permalink: :year/:month/:day/:title/ permalink_defaults: pretty_urls: trailing_index: true trailing_html: true
Directory Settings 1 2 3 4 5 6 7 8 9 source_dir: source public_dir: public tag_dir: tags archive_dir: archives category_dir: categories code_dir: downloads/code i18n_dir: :lang skip_render:
Writing Settings 1 2 3 4 5 6 7 8 9 10 11 12 13 new_post_name: :title.md default_layout: post titlecase: false external_link: enable: true field: site exclude: '' filename_case: 0 render_drafts: false post_asset_folder: true relative_link: false future: true
Home Page Settings 1 2 3 4 5 index_generator: path: 'Home' per_page: 20 order_by: -date
Theme Configuration (themes/your-theme/_config.yml
) Theme configurations vary, but typically include:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 nav: home: / articles: /tags/lesson practice: /tags/practice blog: /tags/blog other: /tags/other links: http://tokipona.org about: /About/ social_links: github: http://github.com/username twitter: https://twitter.com/username linkedin: https://linkedin.com/in/username mail: mailto:email@example.com rss: true google_analytics: enabled: true id: UA-XXXXXXXXX-X
🎭 The Magic of Layouts Layouts are the templates that define how your content is displayed. Hexo uses EJS (Embedded JavaScript) templates by default.
Layout Hierarchy 1 2 3 4 5 6 layout.ejs (main layout) ├── _partial/header.ejs ├── _partial/footer.ejs ├── page.ejs (for pages) ├── post.ejs (for posts) └── archive.ejs (for archives)
Main Layout (layout.ejs
) This is the master template that wraps all other templates:
1 2 3 4 5 6 7 8 9 10 11 12 13 <!DOCTYPE html> <html> <head> <%- partial('_partial/head') %> </head> <body> <%- partial('_partial/header') %> <div class="content"> <%- body %> </div> <%- partial('_partial/footer') %> </body> </html>
Post Layout (post.ejs
) 1 2 3 4 5 6 7 8 <article class="post" itemscope itemtype="http://schema.org/BlogPosting"> <%- partial('_partial/post/gallery') %> <div class="content" itemprop="articleBody"> <%- page.content %> </div> <%- partial('_partial/post/tag') %> <%- partial('_partial/post/category') %> </article>
Page Layout (page.ejs
) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <article class="post" itemscope itemtype="http://schema.org/BlogPosting"> <%- partial('_partial/post/gallery') %> <div class="content" itemprop="articleBody"> <% if (page.search || page.type === "search") { %> <%- partial('_partial/search') %> <% } else if (page.type === "tags") { %> <div id="tag-cloud"> <%- tagcloud({min_font: 12, max_font: 30, amount: 300}) %> </div> <% } else { %> <%- page.content %> <% } %> </div> </article>
Archive Layout (archive.ejs
) This is where we made our custom modification for the “other” tag:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <div id="archive"> <% if (page.tag=="other") {%> <div class="other-intro"> <% var otherPage = site.pages.findOne({path: 'Other/index.html'}); %> <% if (otherPage) { %> <%- otherPage.content %> <% } %> </div> <% } %> <ul class="post-list"> <% page.posts.each(function(post) { %> <li class="post-item"> <%- partial('_partial/post/date', { post: post, class_name: 'meta' }) %> <span><%- partial('_partial/post/title', { post: post, index: true, class_name: '' }) %></span> </li> <% }); %> </ul> <%- partial('_partial/pagination') %> </div>
🔧 Working with Partials Partials are reusable template components that help maintain consistency and reduce code duplication.
Common Partials 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <header id="header"> <a href="/"> <div id="logo" style="background-image: url(<%= theme.logo.url %>);"></div> <div id="title"> <h1><%= config.title %></h1> </div> </a> <div id="nav"> <ul> <% for (var key in theme.nav) { %> <li><a href="<%- url_for(theme.nav[key]) %>"><%= key %></a></li> <% } %> </ul> </div> </header>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <footer id="footer"> <div class="footer-left"> Copyright © <%= date(new Date(), 'YYYY') %> <%= config.author %> </div> <div class="footer-right"> <nav> <ul> <% for (var key in theme.nav) { %> <li><a href="<%- url_for(theme.nav[key]) %>"><%= key %></a></li> <% } %> </ul> </nav> </div> </footer>
Post Title Partial (_partial/post/title.ejs
) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <% if (post.link || post.title){ %> <% if (post.link) { %> <a href="<%- url_for(post.link) %>" target="_blank" itemprop="url"> <h1 itemprop="name"> <%= post.title %> <span class="link-icon">→</span> </h1> </a> <% } else if (post.title) { %> <% if (index) { %> <a class="<%= class_name %>" href="<%- url_for(post.path) %>"> <h1 itemprop="name"><%= post.title %></h1> </a> <% } else { %> <h1 class="<%= class_name %>" itemprop="name"><%= post.title %></h1> <% } %> <% } %> <% } %>
📁 Assets and Static Files Post Assets When post_asset_folder
is enabled in _config.yml
, Hexo creates a folder for each post’s assets:
1 2 3 4 5 6 _posts/ ├── my-post/ │ ├── image1.jpg │ ├── image2.png │ └── diagram.svg └── my-post.md
Referencing assets in your post:
1 2 {% asset_img image1.jpg "Image description" %} {% asset_ img image2.png "Another image" %}
Global Assets For site-wide assets, use the source/
directory:
1 2 3 4 5 6 7 8 9 source/ ├── images/ │ ├── logo.png │ ├── favicon.ico │ └── banner.jpg ├── css/ │ └── custom.css └── js/ └── custom.js
Referencing global assets:
1 2  [Custom CSS ](/css/custom.css )
Theme Assets Theme-specific assets go in the theme’s source directory:
1 2 3 4 5 6 7 8 9 10 themes/your-theme/source/ ├── css/ │ ├── style.css │ └── highlight.css ├── js/ │ ├── main.js │ └── plugins.js └── images/ ├── theme-logo.png └── social-icons.svg
🚀 Advanced Customization Custom Helpers Create custom helpers in themes/your-theme/scripts/helpers.js
:
1 2 3 4 5 6 7 hexo.extend.helper.register('reading_time' , function (content ) { const wordsPerMinute = 200 ; const text = content.replace(/<[^>]+>/g , '' ); const words = text.split(/\s+/ ).length; const minutes = Math .ceil(words / wordsPerMinute); return `${minutes} min read` ; });
Usage in templates:
1 2 3 <div class="reading-time"> <%- reading_time(page.content) %> </div>
Custom Generators Create custom generators in themes/your-theme/scripts/generators.js
:
1 2 3 4 5 6 7 hexo.extend.generator.register('custom_page' , function (locals ) { return { path : 'custom-page.html' , data : locals.theme.custom_data, layout : 'custom_layout' }; });
Custom Filters Create custom filters in themes/your-theme/scripts/filters.js
:
1 2 3 4 5 6 hexo.extend.filter.register('before_post_render' , function (data ) { if (data.title) { data.title = data.title.toUpperCase(); } return data; });
Custom Processors Create custom processors for new file types:
1 2 3 4 5 6 hexo.extend.processor.register('txt' , function (file ) { return { path : file.path, data : file.content.split('\n' ).map(line => `<p>${line} </p>` ).join('\n' ) }; });
🚢 Deployment Strategies GitHub Pages Configure in _config.yml
:
1 2 3 4 deploy: type: git repo: git@github.com:username/username.github.io.git branch: main
Deployment commands:
1 2 3 hexo clean hexo generate hexo deploy
Netlify
Connect your GitHub repository to Netlify
Set build command: hexo generate
Set publish directory: public
Add netlify.toml
to root:
1 2 3 4 5 6 [build] command = "hexo generate" publish = "public" [build.environment] NODE_VERSION = "18"
Vercel
Connect your GitHub repository to Vercel
Set build command: hexo generate
Set output directory: public
Add vercel.json
to root:
1 2 3 4 5 6 7 8 9 10 11 { "builds" : [ { "src" : "package.json" , "use" : "@vercel/static-build" , "config" : { "distDir" : "public" } } ] }
Custom Server For custom server deployment:
1 2 3 hexo generate scp -r public/* user@server:/var/www/html/
💡 Best Practices and Tips 1. Version Control Always keep your Hexo source under version control:
1 2 3 4 5 git init git add . git commit -m "Initial Hexo setup" git remote add origin git@github.com:username/hexo-source.git git push -u origin main
.gitignore file:
1 2 3 4 5 6 7 .DS_Store Thumbs.db db.json *.log node_modules/ public/ .deploy*/
2. Asset Organization Organize your assets logically:
1 2 3 4 5 6 7 8 9 10 11 12 source/ ├── images/ │ ├── posts/ # Post-specific images │ ├── pages/ # Page-specific images │ ├── icons/ # Icons and favicons │ └── banners/ # Header banners ├── css/ │ ├── vendor/ # Third-party CSS │ └── custom/ # Custom CSS └── js/ ├── vendor/ # Third-party JavaScript └── custom/ # Custom JavaScript
Image optimization : Use WebP format and compress images
CSS/JS minification : Enable in _config.yml
Lazy loading : Implement for images
CDN : Use for static assets
1 2 3 4 5 6 7 8 9 10 11 12 13 14 minify: html: enable: true exclude: - '**/player.ejs' css: enable: true exclude: - '**/*.min.css' js: enable: true exclude: - '**/*.min.js'
4. SEO Best Practices
Meta tags : Configure in theme
Sitemap : Install hexo-generator-sitemap
Open Graph : Enable in theme config
Structured data : Add JSON-LD
1 npm install hexo-generator-sitemap --save
5. Security
Environment variables : Use for sensitive data
HTTPS : Enable on your domain
Content Security Policy : Implement headers
Regular updates : Keep Hexo and dependencies updated
6. Backup Strategy
Source backup : Git repository
Content backup : Regular exports
Database backup : If using external databases
Asset backup : Separate backup for media files
🔧 Troubleshooting Common Issues 1. Build Errors Problem : hexo generate
fails
1 2 ERROR Process failed: _posts/my-post.md TypeError: Cannot read property 'length' of undefined
Solution : Check front matter syntax:
1 2 3 4 5 --- title: My Post date: 2025-08-07 15:30:00 tags: [hexo , tutorial ]---
2. Theme Not Loading Problem : Theme changes not reflected
1 2 INFO Validating config WARN No layout: index.html
Solution :
Check theme name in _config.yml
Ensure theme is installed: npm install hexo-theme-name
Clear cache: hexo clean
3. Asset Not Found Problem : Images not displaying
1 GET /images/my-image.jpg 404 (Not Found)
Solution :
Check file paths
Use asset tags: {% asset_img my-image.jpg %}
Verify file exists in correct directory
4. Deployment Issues Problem : GitHub Pages deployment fails
1 ERROR Deployer not found: git
Solution : Install deployer:
1 npm install hexo-deployer-git --save
Problem : Site loads slowly
Solution :
Optimize images
Minify CSS/JS
Enable caching
Use CDN
6. Plugin Conflicts Problem : Plugins not working together
1 ERROR Plugin load failed: hexo-plugin-name
Solution :
Check plugin compatibility
Update plugins
Disable conflicting plugins temporarily
🎯 Conclusion: Mastering Your Hexo Blog Understanding Hexo’s structure is like having a map to your blog’s architecture. It empowers you to:
Customize with confidence : Know exactly which files to modify
Troubleshoot effectively : Understand how components interact
Extend functionality : Add new features seamlessly
Maintain efficiently : Keep your blog organized and scalable
The Learning Journey Remember, mastering Hexo is a journey. Start with the basics:
Understand the directory structure
Learn to modify templates
Experiment with configurations
Gradually tackle advanced customizations
Resources for Continued Learning
Final Thoughts Hexo strikes the perfect balance between simplicity and power. Its modular architecture means you can start simple and gradually add complexity as needed. Whether you’re running a personal blog, a professional portfolio, or a complex content site, understanding Hexo’s structure gives you the foundation to build something truly remarkable.
“The beauty of Hexo lies not just in what it can do out of the box, but in what it enables you to create when you understand how it works.”
📚 Quick Reference Cheat Sheet Essential Commands 1 2 3 4 5 6 hexo new "Post Title" hexo new page "Page Name" hexo generate hexo server hexo deploy hexo clean
Directory Structure 1 2 3 4 5 6 7 8 9 10 11 12 13 source/ # Your content ├── _posts/ # Blog posts ├── about/ # Custom pages └── images/ # Images and assets themes/ # Theme files └── theme-name/ ├── layout/ # Templates ├── source/ # Theme assets └── _config.yml # Theme config _config.yml # Main configuration package.json # Dependencies and scripts
Common Template Variables 1 2 3 4 5 6 <%= config.title %> # Site title <%= page.title %> # Page title <%= page.content %> # Page content <%= page.date %> # Page date <%= post.tags %> # Post tags <%= post.categories %> # Post categories
Helper Functions 1 2 3 4 5 <%- url_for(path) %> # Generate URL <%- partial('partial-name') %> # Include partial <%- date(date, format) %> # Format date <%- tagcloud(options) %> # Generate tag cloud <%- list_categories() %> # List categories
Happy blogging with Hexo! May your static sites be fast, your content be engaging, and your deployment process be smooth. 🚀