CLI tool
zenn-dev/zenn-editor avatar
zenn-dev/zenn-editor

zenn-editor, the Markdown packages behind the Zenn article preview

Convert markdown to html in Zenn format

749 stars87 forksTypeScriptMIT

At a glance

What is it?
zenn-dev/zenn-editor is a monorepo of five packages that reproduce Zenn's Markdown rendering, including a Markdown to HTML converter, the preview stylesheet, the embedded element web components and a model layer for article and book data. The README also warns that the project does not follow strict semantic versioning, so breaking changes arrive in minor releases.
Who is it for?
Adopt zenn-editor if you are building a preview, an editor or an agent around Zenn's Markdown and want the rendering to match the platform exactly. Skip it if you just need a Markdown to HTML converter, since a general library does that without inheriting Zenn's conventions.
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 2 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 October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the monorepo contains

This repository groups together the packages relating mainly to Zenn's Markdown, and the README says that by using them you can build surrounding systems such as the Zenn CLI or the Zenn VS Code extension. That is the framing: these are the building blocks of the Zenn toolchain rather than a general Markdown library.

Five packages are listed. zenn-cli is the command line tool for viewing local articles and books. zenn-content-css is the stylesheet used when previewing Markdown. zenn-embed-elements are the embedded elements that run in the browser, implemented as web components. zenn-markdown-html converts Markdown to HTML. And zenn-model handles the data for articles and books.

The division is sensible. The converter produces HTML, the stylesheet makes it look like Zenn, the web components handle the things that need JavaScript to work, and the model layer is what a CLI or an editor needs to read and write the content files.

The warning at the top of the README is the most important line for anyone depending on this. The project does not follow strict semantic versioning, and breaking changes are currently handled in minor version bumps. The changes are documented on the releases page.

That is a significant caveat and it is stated plainly rather than buried. If you depend on these packages, you cannot rely on a caret range to mean compatible upgrades, and you have to read the release notes before bumping. In practice that means pinning exact versions and treating an upgrade as a deliberate step.

The default branch is canary, which is worth noting. The version numbers on the releases page run 0.5.7 with several 0.5.7-alpha builds on the same day as the last push, so the canary branch is where the alpha builds come from, and the released 0.5.7 is the stable one.

Rendering Markdown the Zenn way

Using Zenn's Markdown requires combining several packages, and the README walks through the steps in order.

The first is the converter. You import markdownHtml and pass the Markdown to it:

tsx
import markdownHtml from 'zenn-markdown-html';

and the conversion itself is a single call:

ts
const html = markdownHtml(markdown);

The README is explicit about a constraint that would otherwise cost you an afternoon. This processing only runs on Node.js. Running it in the browser throws an error.

That constraint is why the React example uses a component that receives the HTML as a prop rather than converting in the browser, and it is a sensible design for a converter that depends on Node's module resolution.

The second step is the stylesheet, imported for its side effect:

ts
import 'zenn-content-css';

The third is the embed script, which is what enables the embedded notations such as tweets and Mermaid:

html
<script src="https://embed.zenn.studio/js/listen-embed-event.js"></script>

That script goes in the head tag, and the README warns that adding defer or async attributes may break the embedding when a single page application navigates, because the script would run after the content that expects it.

The fourth step, only needed if you use KaTeX notation, is the embed elements package. Because zenn-embed-elements does not support server-side rendering, it has to be loaded on the client side, and the example shows it being imported inside a useEffect:

ts
import 'zenn-content-css';

export default function App(props) {
  useEffect(() => {
    import('zenn-embed-elements');
  }, []);

  return (
    <div
      className="znc"
      dangerouslySetInnerHTML={{
        __html: props.html,
      }}
    />
  );
}

One detail appears in both examples and is easy to miss: the container element needs the class name znc. The README says that is required in order to apply the CSS, and the React example carries the comment noting that the class name must be specified. The CSS is scoped to that class, so an element without it renders unstyled.

How it compares with a general Markdown converter

The obvious alternative is a general purpose Markdown library, and the difference in approach is that zenn-markdown-html is not trying to implement Markdown. It implements Zenn's dialect of it.

Zenn extends Markdown with its own notations: message callouts, embed notations for tweets, YouTube videos and Mermaid diagrams, KaTeX formulas, and the fenced code block with a filename. A general library will render the standard Markdown correctly and either ignore the extensions or treat them as literal text, so the output looks wrong in specific and hard-to-diagnose ways.

That is the case for these packages. If your product needs to preview Zenn content faithfully, the general converter is not a substitute, and the difference is not about quality of implementation but about which dialect you are implementing.

The second difference is the styling. zenn-content-css is the stylesheet that makes the rendered HTML look like Zenn, with the code block presentation, the callout colours, the embed frames and the rest. Reproducing that by hand means reading Zenn's own stylesheet and copying the rules, which is exactly what the package is.

The third difference is the embed elements. A tweet embed in Zenn is not just a link, it is a rendered card, and that card is a web component rather than HTML. General converters do not emit custom elements, and they cannot, because the components are part of this repository.

What you give up is portability. The output is Zenn-shaped, and a reader that is not Zenn will not have the CSS or the components, so the same HTML renders differently depending on where it is displayed. For a preview or an editor inside the Zenn ecosystem that is irrelevant. For a general publishing tool it is a serious limitation.

The counter-consideration is the versioning policy, which is the same argument as before and applies with more force here, because these packages are the rendering path rather than a side dependency. A minor version bump that changes the output HTML changes what your users see, and there is no major version boundary to signal it. Pinning exact versions and reading the release notes is the only safe way to upgrade.

The agent skills and what they add

The repository also ships a skill for AI agents, and this is the newest thing in it.

The zenn-cli skill, described as living at skills/zenn-cli/SKILL.md, holds the procedures for using the Zenn CLI for scraping operations, setup, environment variables and safety checks. The README gives two ways to pull it into a project.

The first is the npx skills route:

bash
npx skills add zenn-dev/zenn-editor --skill zenn-cli

which opens an interactive selection of the target agent, and the README points at the skills documentation for details.

The second is npx rulesync:

bash
npx rulesync fetch zenn-dev/zenn-editor --skills zenn-cli
npx rulesync generate --targets claudecode --features skills

That fetches the skill into a .rulesync/skills/zenn-cli/ directory and generates the placement for the agent. The README notes the example is for Claude Code and that the targets option should be set to match the tool you use, and that if you already have a rulesync.jsonc with the target tools configured, npx rulesync generate --features skills will use that configuration instead.

The value of this for a team is that the knowledge of how the Zenn CLI behaves, including the environment variables and the safety checks, is written down in a form an agent can follow, rather than being tribal knowledge in a maintainer's head. It is the same move as publishing a skill file for any tool: the agent gets the procedure instead of guessing at the commands.

The developer documentation is at zenn-dev.github.io/zenn-docs-for-developers, which is separate from the package READMEs, and the licence is MIT.

For a team building around Zenn content, the practical sequence is straightforward. Render with zenn-markdown-html on the server, ship zenn-content-css with the output, load the embed script in the head without defer or async, and add zenn-embed-elements on the client if you use KaTeX. Pin every version exactly, and check the releases page before each upgrade, because the minor version is where the breaking change lives.

Editorial conclusion

Adopt zenn-editor if you are building a preview, an editor or an agent around Zenn's Markdown and want the rendering to match the platform exactly. Skip it if you just need a Markdown to HTML converter, since a general library does that without inheriting Zenn's conventions. Verify first by rendering a document that uses Zenn's own syntax, such as an embed or a message callout, and confirming the output matches what the Zenn site shows, then pin the exact package versions because the minor releases carry breaking changes.

Frequently asked questions

Which packages does the zenn-editor repository contain?

Five: zenn-cli, a command line tool for viewing local articles and books, zenn-content-css, the Markdown preview stylesheet, zenn-embed-elements, the embedded elements implemented as web components, zenn-markdown-html, which converts Markdown to HTML, and zenn-model, which handles article and book data.

Can zenn-markdown-html run in the browser?

No. The README states that this processing can only run on Node.js and that running it in the browser produces an error, which is why the examples render on the server and pass the HTML into the client as a prop.

What does the znc class name do?

It is required on the element wrapping the generated HTML, because the preview stylesheet from zenn-content-css is scoped to it. The React example in the README carries a comment noting that the class name must be specified for the CSS to apply.

Can I upgrade zenn-editor packages with a caret version range?

Not safely. The README warns that the project does not follow strict semantic versioning and that breaking changes are currently handled in minor version bumps, so you need to read the releases page and pin exact versions.

What does the zenn-cli agent skill cover?

The skill at skills/zenn-cli/SKILL.md documents the procedures for using the Zenn CLI for scraping operations, setup, environment variables and safety checks, and it can be installed into a project with npx skills add zenn-dev/zenn-editor --skill zenn-cli or fetched and placed with npx rulesync.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. zenn-dev/zenn-editor on GitHub
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/zenn-dev-zenn-editor.svg)](https://hysenlabs.com/projects/zenn-dev-zenn-editor)

Was this useful?