Adding Hot Reload to Hexo
hexo server natively watches for file changes and regenerates pages, but unfortunately it never tells the browser to refresh. After every edit, we still have to switch back to the browser and hit F5 or ⌘ + R.
I had used Astro before, and it could do this. Since it was clearly feasible, I asked CodeX to produce a script: user updates a .md file -> Hexo regenerates the page -> the browser automatically refreshes the current http://localhost:4000 page.
I’ve never studied frontend systematically, but from using tools like Astro and Vite I’d already experienced this “hot reload” feature, so I was fairly sure the idea was doable. I asked CodeX to write a script that implements: user updates .md -> Hexo regenerates the page -> browser refreshes automatically.
Experiences: Wish I Had This Sooner
With the script in place, I can now split my screen: editing the md file on the right, and Edge/Chrome/Zen/Safari showing a live preview on the left. The improvement to the experience boils down to:
- Most directly, no more manual refreshing. Since the blog couldn’t hot-reload in the browser before, I usually made do with the editor’s built-in preview
- Previewing in the browser is true, out-of-the-box WYSIWYG
WYSIWYG: Browser or Editors?
On the WYSIWYG point, mainstream editors all ship with Markdown preview these days, but the results are underwhelming. Typora looks nicer, but its editing experience falls a bit short of Obsidian/VSCode/NeoVim/Zed.
Also, in pursuit of better UI/UX, I often stitch HTML code into my Markdown. Most editors’ preview engines choke on these custom tags and fail to render them correctly, which of course isn’t the editors’ fault.
Some cases that fail to render correctly:
- Shiki syntax-highlighting Transformers: a plugin in Obsidian can partially render them, but its syntax follows the Expressive Code standard
- Some markdown-it plugins: popular ones like callout/math/mermaid preview fine in Obsidian/Typora, but the less common yet very practical tab component doesn’t. Obsidian’s wikilinks have the same problem
- Custom HTML Elements (Web Components): see this post for how custom HTML Elements are used; the components defined on this site are showcased on this page
- CSS styles embedded in Markdown can’t be previewed either
Appendix: Source Code
Hexo automatically loads scripts placed in scripts/ under the project root or the theme directory. I chose to create scripts/hot-reload.js in the theme’s /scripts directory
Since GitHub offers a better code-reading experience and stays more up to date, I won’t paste the code here
Appendix: How It Work
Since this was vibe-coded, naturally I have no idea how it works; as long as it works 🤓
Just ask the AI for a straight, no-dodging explanation, do we even need one?
BTW, the output has been lightly edited and trimmed; the model I actually asked was Gemini in AI Studio, but its logo is too ugly, so I’m swapping in OpenAI’s logo for now
At its core, the script uses SSE (Server-Sent Events), a technology that lets the server push real-time notifications to the browser.
Environment detection (
isHexoServerCommand)The script first checks whether it is running under
hexo servermode. This ensures that these development-only bits are not injected during a statichexo generatebuild or a production deployment.Listening to the Hexo lifecycle
hexo.on('generateAfter'): fires when Hexo finishes regenerating pages. Every time Hexo completes a regeneration, the script triggers a refresh. Since the Hexo server automatically regenerates on file changes, listening to this single event is enough.
Debounce & Cooldown
To prevent frequent editor saves (such as auto-save) from making the browser refresh like crazy, the script adds a
150 msdebounce and a500 mscooldown interval, keeping refreshes both timely and stable.Middleware injection (
server_middleware)The script opens a special “secret handshake” endpoint inside Hexo’s built-in server:
/__hexo_live_reload. The browser stays connected to this endpoint, waiting for the server’s signal.Frontend injection (
injector)Using Hexo’s
injectorfeature, the script automatically inserts a tiny piece of JavaScript at the end of every page’s<body>. This code listens for thereloadsignal from the server and callswindow.location.reload()when it arrives.What is
window.location.reload? What is__hexo_live_reload?1. What iswindow.location.reload()?This is a method provided by the browser’s native JavaScript, and what it does is very straightforward: it refreshes the current page.
- Effect: Equivalent to pressing F5 on your keyboard, or clicking the “refresh” button next to the browser’s address bar.
- Role in the script: When the browser receives the “file updated” message from the server, it runs this line. You no longer have to click refresh yourself; the page automatically updates to the latest state.
2. What is/__hexo_live_reload?This is a virtual communication address (Endpoint).
In a normal Hexo preview, visiting
http://localhost:4000shows your blog. But this script quietly opens a hidden “private line” in the background:http://localhost:4000/__hexo_live_reload.Here is how it works:
It’s a “pipe”: This address doesn’t exist on your disk. It is a long-lived connection channel (based on SSE) created by the Node.js script intercepting requests.
Server side (Hexo): When you see
req.url === LIVE_RELOAD_PATHinscripts/live-reload.js, it is telling Hexo: “If a browser requests this address, don’t serve it page content — keep the connection open and stand by to send signals.”Client side (browser): The script puts
new EventSource('/__hexo_live_reload')into the page. This line tells the browser to keep “listening” on this address, like waiting by the phone.
Summary: How Do the Two Work Together?Connect: The browser opens your blog and automatically connects to the
/__hexo_live_reloadpipe.Wait: The browser waits at the other end of the pipe; nothing happens.
Trigger: You edit a
.mdfile, and Hexo regenerates the page.Signal: The Hexo server pushes a message into the
/__hexo_live_reloadpipe:{"type": "reload"}.Action: The browser receives the message on its end and immediately calls
window.location.reload().Result: The page is fresh.
AuthorGnixAijTitleAdding Hot Reload to HexoTranslationLLM · unreviewedMarkdown source/en/hexo_hot_reload/index.mdCreated2026-04-08 00:00Updated2026-07-08 21:17Preferences
Theme
Typography
SizeLine HeightCompactRelaxedWeight[Preview] When You Are Old
When you are old and grey and full of sleep, and nodding by the fire, take down this book,
And slowly read, and dream of the soft look your eyes had once, and of their shadows deep.