Framework
hexojs/hexo avatar
hexojs/hexo

Hexo 8.1.2: a globally installed CLI wrapped around a TypeScript library

GitHub describes it as A fast, simple & powerful blog framework, powered by Node.js.. The repository metadata lists TypeScript as its primary language. The metadata lists the MIT license. This article stays within the project description and details documented in the GitHub repository README.

41,775 stars45 forksTypeScriptMIT

At a glance

What is it?
Hexo is an MIT-licensed blog framework powered by Node.js, installed as a global CLI and used to generate a static site. The interesting parts are in the package rather than the feature list: the CLI ships separately from the library, the published tarball contains only dist/ and bin/, the default test script globs one directory, and 22 runtime dependencies do the work of six of the hexo packages plus a set of older libraries.
Who is it for?
Hexo suits a writer who wants posts in Markdown, a local hexo server for preview, and a directory of static files to hand to any host, with the last push on 2026-08-29 and version 8.1.2 released on 2026-05-06.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 33 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

hexo-cli is installed globally, hexo is what your blog pins

The install step and the runtime are two different packages. The documented command is:

bash
$ npm install hexo-cli -g

with a Homebrew alternative for macOS and Linux:

bash
$ brew install hexo

Only after that do you create a site with hexo init blog and cd into it. Nothing in the quick start installs a package called hexo into the blog directory, yet the published package is named hexo, and its own dependency list includes hexo-cli at ^4.3.2.

Consequence for you: the binary you type is whatever version the global install put on your PATH, and nothing in your blog's package.json constrains it. A project pinned to one release can still be driven by a different CLI than the one its author tested with, and the mismatch shows up as a command that exists in one version and not the other. Version-check the CLI itself before you blame a blog.

The published tarball is dist/ and bin/ and nothing else

package.json names the compiled entry point as main: dist/hexo, points types at ./dist/hexo/index.d.ts, maps the hexo command to ./bin/hexo, and restricts the published files to dist/ and bin/. The build is a single TypeScript project reference build, `tsc -b`, with `tsc -b --clean` for the reset and a prepublishOnly script that installs, cleans, then builds before anything ships.

Consequence for you: the npm artifact is compiled JavaScript plus one executable, so the TypeScript sources under lib/, the test directory, and the mocha configuration are not in the tarball. A bug report against a hexo release therefore starts from a stack trace in generated code, and anyone who wants to read the implementation of a method has to clone the repository rather than inspect node_modules. The upside is install size, the downside is that the published version and the readable source are two different artifacts.

The default test script globs one directory, and coverage is lcov only

The test entry point is a single line: mocha over test/scripts/**/*.ts with ts-node/register required, so the suite runs the TypeScript sources directly instead of the built output. Coverage is a separate script that wraps the same run in c8 with the lcovonly reporter, and the test command is also exposed as test-cov with the parallel flag switched off.

Consequence for you: the default glob reaches test/scripts and nothing else, so a test placed in another directory under test/ does not run unless you widen the pattern yourself. The lcovonly reporter is what the Coveralls badge consumes, and it produces no human-readable terminal summary, which makes a local coverage check less convenient than a text reporter would be. The trade is CI-oriented tooling: the suite is shaped for the badge on the front page rather than for a developer's terminal.

npm install in a clone runs husky through the prepare script

The repository wires its own checks into the install path. The prepare script is husky, the hooks live in .husky/, staged-file rules sit in .lintstagedrc.json, mocha has its own .mocharc.yml, and linting is configured in eslint.config.js with the script scoped to `eslint lib test`. Formatting conventions live in .editorconfig.

Consequence for you: an ordinary npm install in a fresh clone installs the hook runner as a side effect, and in an environment that installs with scripts disabled, such as a hardened CI image, those hooks silently do not exist. Linting is also not part of the test command, so nothing in the default test run will tell you that a style rule was broken; it has to be run as its own script, against lib and test only, which means files outside those two directories are outside the checked set.

Twenty-two runtime dependencies, six of them from the hexo family

The runtime list is longer than a blog framework's reputation suggests: abbrev, bluebird, fast-archy, fast-text-table, hexo-cli, hexo-front-matter, hexo-fs, hexo-i18n, hexo-log, hexo-util, js-yaml, js-yaml-js-types, micromatch, moize, moment, moment-timezone, nunjucks, picocolors, pretty-hrtime, strip-ansi, tildify, titlecase, and warehouse.

Six of those are the hexo family, which is the framework's own modular split: front matter parsing, filesystem access, logging, translations, shared utilities, and the CLI each version independently under caret ranges. The rest includes bluebird and moment with moment-timezone for promises and dates, nunjucks for templating, micromatch for globs, and warehouse as the data layer.

Consequence for you: a blog inherits a supply chain and an update schedule it did not choose, and because the framework is assembled from independently versioned hexo packages, a caret range resolving upward is how your site changes without any commit on your side.

One-command deploy names two targets and leaves the rest open

The feature list promises one-command deploy to GitHub Pages, Heroku, etc. Those two names are the only concrete targets given, and the etc. is left to the plugin list linked a few lines below. The documented commands around it stop at the local workflow: hexo init blog, then hexo server to preview, hexo new "Hello Hexo" to create a post, and hexo generate to produce the static files.

Consequence for you: the generate step is the one this package owns, and the deploy step is named without the command, the plugin, or the credential handling behind it. A reader who takes one-command deploy literally will finish hexo generate with a directory of files and still need to find where the deploy lives. Check which deploy plugin you are installing and what it expects before you promise a one-step release.

The 8.1.2 in package.json is older than the last push

Two dates matter and they are not the same. The version in package.json is 8.1.2, and 8.1.2 was released on 2026-05-06. The last push to master was 2026-08-29. Before that, 8.1.1 shipped on 2025-10-31 and 8.1.0 on 2025-10-26, so two patch releases five days apart were followed by a six month gap before the next one.

Consequence for you: what npm gives you is 8.1.2, and what you see on the default branch is something newer that has not been tagged. When you read code on master to understand a release, you are reading a version nobody installs yet, and when you report a problem against a tag, the maintainer may not see the same line. Pin the release you actually depend on, and keep the two views apart when you file anything.

The extension API lives on hexo.io, not in the tree

The top level of the repository is .editorconfig, .github/, .gitignore, .husky/, .lintstagedrc.json, .mocharc.yml, CODE_OF_CONDUCT.md, LICENSE, README.md, bin/, eslint.config.js, lib/, package-lock.json, package.json, test/, and tsconfig.json. There is no documentation directory. The API reference, the installation guide, the contribution guide, the plugin list, the theme list, and the troubleshooting page are all links to hexo.io, and the front page also points at the Awesome Hexo list.

Consequence for you: extensibility is real, since the feature list claims an API for limitless extensibility and support for most Octopress plugins, but every page describing how to write a plugin lives off the repository. A fork with no website becomes a framework with no documented extension surface, and a site built on plugins carries versions that the framework's repository never sees. Budget for reading hexo.io/api/ before you write the first plugin, and treat plugin compatibility as your own maintenance job.

Editorial conclusion

Hexo suits a writer who wants posts in Markdown, a local hexo server for preview, and a directory of static files to hand to any host, with the last push on 2026-08-29 and version 8.1.2 released on 2026-05-06. It does not suit someone who wants the framework's documentation, its API reference, or its deployment recipes inside the repository, because all three live on hexo.io, and it does not suit a team that wants a single global toolchain, since the CLI and the library version separately. Before you commit, run the version check on your own machine, read which deploy plugin you are actually installing, and confirm that the theme and plugins you pick are maintained against the release you pinned, not against master.

Frequently asked questions

What is Hexo used for?

Hexo is a blog framework powered by Node.js, used to write posts and generate a static site. The documented workflow is hexo init blog to create a site, hexo server to preview it locally, hexo new to add a post, and hexo generate to produce the static files, with support for GitHub Flavored Markdown and most Octopress plugins.

Is Hexo a static site generator?

In practice it generates a directory of static files rather than serving pages from a running process, since hexo generate is the step the README documents for producing them. The feature list pairs that with one-command deploy to GitHub Pages, Heroku, and similar targets, plus hundreds of themes and plugins.

How do I deploy a Hexo site?

The feature list names GitHub Pages and Heroku as the targets of its one-command deploy feature and points at the plugin list for the wider ecosystem, so the deploy command itself comes from the documentation at hexo.io rather than from this page. A troubleshooting page is published at hexo.io/docs/troubleshooting.html.

What alternatives are there to Hexo?

The repository names none. It points outward to its own ecosystem instead: the Awesome Hexo list, the plugin list and theme list on the wiki, and the documentation at hexo.io, so a comparison with other blog frameworks has to be made outside this page.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/hexojs-hexo.svg)](https://hysenlabs.com/projects/hexojs-hexo)