Full install & authoring guide: README.md · 简体中文
Server-side code highlighting for WordPress, powered by Shiki.
When a post is saved, the plugin sends each code block to the local NodeCode service
(../nodecode-service), receives themed static HTML, and caches it in post meta. On the
frontend it serves that cached HTML with zero JavaScript required for highlighting
— only a tiny script for the copy button, soft-wrap toggle and collapse.
Licensed under the MIT License (see LICENSE).
Shiki is a JavaScript library with no PHP port. Running it as a small localhost service lets WordPress reuse the exact, high-fidelity TextMate-grammar highlighting that VS Code uses, while visitors still get plain cached HTML.
Author saves post ──> WP (save_post) ──> POST /highlight/batch (localhost)
└── cache HTML in post meta (_nodecode_cache)
Visitor views post ──> the_content ──> read cached HTML ──> static themed <pre>
- WordPress 5.8+, PHP 7.4+
- The companion NodeCode service running (see
../nodecode-service/README.md)
- Start the NodeCode service and confirm
curl http://127.0.0.1:9527/health. - Copy this
nodecode-pluginfolder intowp-content/plugins/. - Activate NodeCode in the WordPress admin.
- Go to Settings → NodeCode and:
- Set the Service URL (default
http://127.0.0.1:9527) and API Key (must matchNODECODE_API_KEYin the service.env). - Click Test service connection.
- Choose Light theme / Dark theme (must be preloaded by the service).
- Confirm the Dark mode CSS selector matches your theme setup (see below).
- Set the Service URL (default
Shiki renders both themes into CSS variables (--nodecode-light / --nodecode-dark)
in the same cached HTML; light is shown by default and the plugin decides when to go
dark based on the Dark mode strategy setting:
| Strategy | When code blocks go dark | Use when |
|---|---|---|
| Follow site dark toggle (selector) — default | When the Dark mode CSS selector matches (a class/attribute on <html>/<body>) |
Your theme or a WP dark-mode plugin toggles a class |
| Follow visitor OS | When the visitor's OS is dark (prefers-color-scheme) |
Your site has no dark toggle of its own |
| Always light | Never (blocks stay light) | A dark plugin inverts the whole page with a CSS filter, or you use Dark Reader site-wide |
If you run several dark-mode mechanisms at once (theme dark mode + a WP dark-mode plugin + a browser extension like Dark Reader), they can fight and double-process the code colors. The plugin includes safeguards and a recommended setup:
- Every code block declares
color-schemeand carries adata-nodecode-managed="1"attribute, signalling browsers/extensions that it handles its own theming. - Pick ONE source of truth via the strategy setting instead of stacking them.
Recommended config for a site that has its own dark toggle:
- Strategy = Follow site dark toggle (selector).
- Toggle dark mode on your site, open DevTools, inspect
<html>/<body>, and put the class/attribute that appears into Dark mode CSS selector (default trieshtml[data-theme="dark"], body.dark, html.dark). - If a separate dark plugin or Dark Reader still recolors code blocks, exclude
them there using the selector
[data-nodecode-managed](most tools have an "ignore selectors" list), or switch the strategy to Always light so the page-level filter darkens everything uniformly.
Any of these are detected automatically:
- Gutenberg code block (
```block) — language taken from alanguage-xxxclass if present. - Classic editor
<pre><code class="language-js"> ... </code></pre>(also supports legacybrush: js). - Shortcode:
[nodecode lang="js" title="example.js"] ... [/nodecode]or[code lang="php"] ... [/code](legacy[shiki]is also accepted).- Optional attributes:
title,linenumbers="true|false",wrap="true|false".
- Optional attributes:
Inside the code, comments drive per-line effects:
// [!code highlight]— highlight the line// [!code ++]/// [!code --]— diff added / removed// [!code focus]— focus this line (blur the rest)// [!code word:foo]— highlight a word
After changing themes/features, regenerate caches for existing posts:
wp nodecode rerender --all
wp nodecode rerender --post=123
wp nodecode rerender --all --post_type=post,page
wp nodecode flush # clear all cached HTML + transients
wp nodecode health # check the service connection- Each block is hashed from
code + lang + title + flags + config version. config versionincludes the chosen themes and plugin version, so changing themes invalidates old cache entries automatically (re-runwp nodecode rerender).- If the service is down when a post is saved, those blocks are not cached;
on the frontend the plugin first tries a live render (cached in a transient),
and if that also fails it falls back to an escaped
<pre><code>block — the page never breaks.
Removing the plugin deletes its options, all _nodecode_cache post meta and the
nodecode_live_* transients.