Open-source project
wooorm/starry-night avatar
wooorm/starry-night

starry-night: GitHub-style JavaScript syntax highlighting

Syntax highlighting, like GitHub

1,816 stars39 forksJavaScriptMIT

At a glance

What is it?
@wooorm/starry-night turns source code into a GitHub-style hast syntax tree using TextMate grammars and Oniguruma. It fits documentation sites and JavaScript applications that need themeable classes or an AST, but its WASM runtime and grammar payload can be too heavy for browsers.
Who is it for?
Adopt @wooorm/starry-night when GitHub-compatible highlighting, themeable classes, and AST output matter more than bundle size. Do not choose it for a browser that must avoid the WASM and grammar payload without careful budgeting.
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 69 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

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

From daily snapshots

Star history

  • wooorm/starry-night1,816 ★ · -1
Star history
Repository2026-10-172026-11-07
wooorm/starry-night1.8k1.8k

-1 Stars in the last 21 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Why starry-night targets GitHub-like code presentation

@wooorm/starry-night addresses a specific gap between GitHub-style highlighting and smaller JavaScript highlighters. GitHub uses a closed-source highlighter called `PrettyLights`, while starry-night provides an MIT-licensed JavaScript implementation based on the TextMate grammar ecosystem used by editors such as Sublime Text, Atom, and VS Code. Its goal is GitHub-compatible presentation, not visual cloning of every GitHub page feature.

The package supports more than 600 grammars, including languages such as MDX, and returns structured objects rather than a finished HTML string. That output is useful when code needs to appear in a terminal through ANSI rendering, in a virtual DOM application such as React or Preact, or inside a `hast` and `rehype` pipeline. It also emits CSS classes associated with the shipped styles, making light and dark themes possible without requiring inline styles for every token.

This makes the package most relevant to documentation platforms, developer tools, code review interfaces, static documentation generators, and code-heavy web applications. It is a poor fit for a small page that only highlights one common language and does not need GitHub-compatible grammar coverage. The deciding feature is the output model and visual contract, not simply the number of supported languages.

The TextMate grammar and WASM path inside starry-night

The processing path begins when a language flag is resolved with `flagToScope`. For example, `markdown` is looked up through the language information associated with `github-linguist`, which maps it to the scope used by a TextMate grammar. `highlight(value, scope)` then applies that grammar to the source text.

The runtime dependencies listed in `package.json` include `vscode-textmate` and `vscode-oniguruma`. Oniguruma supplies the regular-expression engine required to interpret TextMate grammars, and its WebAssembly binary accounts for the package's WASM dependency. The grammar definitions in `lang/` determine the scopes assigned to tokens. Those scopes become properties on element nodes in the resulting syntax tree.

The result is a `hast` AST with `root`, `element`, and `text` nodes. Element nodes use `span` tags and class names that can be mapped through the package's stylesheets. This separation between tokens and CSS is important. A renderer can serialize the AST to HTML, translate its nodes into React elements, or handle it differently without losing the highlighting information.

The package can also create a runtime from a selected grammar set through `createStarryNight(grammars)`. This makes grammar inclusion an explicit application decision. It is not necessary to load every supported language into every build.

Installing @wooorm/starry-night and highlighting the first line

The package is ESM only, and the README documents Node.js version 16 or newer. Install it as a project dependency with the following command. The command installs the package into the current npm project and records it according to the package manager's normal behavior.

bash
npm install @wooorm/starry-night

Create an ESM module that imports `common` and `createStarryNight`. The `common` set is the README's starting point for sites that need a moderate selection of languages. `createStarryNight(common)` initializes the highlighter, `flagToScope('markdown')` resolves the requested language, and `highlight` returns the AST.

js
import {common, createStarryNight} from '@wooorm/starry-night'

const starryNight = await createStarryNight(common)
const scope = starryNight.flagToScope('markdown')
const tree = starryNight.highlight('# hi', scope)

console.log(tree)

The documented output is an object whose root contains children representing the highlighted source. The exact token markup can vary with the grammar, but the important check is that `tree` is a structured `hast` tree rather than an HTML string. A missing grammar is not something to repair by guessing a scope. First verify that the selected grammar list contains the language, then resolve its documented flag.

For Deno, the README provides an `esm.sh` import. For browsers, it also documents a bundled `esm.sh` import and a stylesheet URL that supports light and dark mode automatically. Those browser examples make network delivery easy, but they do not remove the bundle-size constraint described below.

Rendering the starry-night tree in an application

The AST is the main integration boundary. A server-side documentation pipeline can serialize it as HTML, while a browser application can convert the nodes into virtual DOM elements. This separation avoids committing the application to one renderer, although it also means the application still needs a way to display the tree.

The class names are part of the design. The README positions starry-night as similar to Shiki because both use related underlying dependencies, but their delivery models differ. starry-night emits classes and ships CSS, which is useful when dark mode and alternate themes must be selected through stylesheets. Shiki is not described here as an interchangeable API, so its exact behavior should be checked separately before comparing migration details.

The tree also fits the `unified`, `remark`, and `rehype` ecosystem because `hast` is its output format. Integrations with `markdown-it` are also listed in the README. For applications that already work with syntax trees, this can reduce the need to parse generated HTML back into nodes. For applications that only want a static string, the extra rendering step may not justify the design, but the documented output remains more flexible than serialized HTML.

The source tree includes TypeScript declarations, grammar files, runtime support files, styles, and tests. That layout indicates that language support, rendering utilities, styling, and runtime behavior are maintained as separate concerns. Consumers should treat changes to grammar availability and CSS class behavior as parts of the integration surface.

starry-night bundle choices and the browser tradeoff

Payload selection is the package's most concrete deployment constraint. The README reports that the bundled, minified, and gzipped package plus WASM binary is 185 kB. Adding the `common` grammar list contributes about 250 kB, while `all` contains roughly 600 languages and adds about 1.6 MB. Individual languages are typically 3 to 5 kB, with documented examples of Astro at 2.1 kB and TSX at 25.4 kB.

Those figures make `common` a sensible initial choice for a site with ordinary language needs, but they do not make the full library automatically appropriate for a browser. A browser application must account for the WASM download, grammar data, initialization work, and the code needed to turn the tree into DOM elements. A static site can sometimes hide some of this cost behind its build process; an application that initializes the highlighter after a user enters a page faces a different loading profile.

The package permits manual grammar selection, which is the most direct way to control this cost. It also permits adding languages to `common`. That choice should be based on actual language demand. Loading `all` for a product that displays only JavaScript and Markdown adds grammars without adding a visible feature.

The CSS is separately available through the package export path `./style/*`. The README's browser example uses a stylesheet from `esm.sh` for automatic light and dark mode. If the application uses its own styling pipeline, the relevant question is whether it can preserve the package's scope classes and reproduce the required visual treatment.

When starry-night is the wrong highlighter

The README explicitly identifies browsers as a case where starry-night might be too heavy, especially because of its WASM dependency and large grammars. It names `lowlight` and `refractor` as more suitable alternatives in that situation. Their different approach is relevant: instead of using starry-night's GitHub-oriented grammar bundle and WASM-backed runtime, they offer alternative JavaScript highlighting paths for projects that prioritize lower browser weight or a different ecosystem.

The package is also not intended to reproduce GitHub's newer `TreeLights` behavior. According to the README, GitHub has been moving some languages toward the closed-source `TreeLights`, based on TreeSitter, while `PrettyLights` continues to handle other languages. starry-night documents itself as a `PrettyLights`-style implementation, so differences from GitHub are possible for languages or markup details where GitHub uses the newer system. The README also notes small markup differences from `PrettyLights` itself.

A team that requires exact parity with GitHub's current output should not assume that matching class names guarantees identical markup. A team that needs a tree for React, `hast`, or terminal rendering has a different requirement from a team that only wants a colored HTML string. Those requirements should be checked before choosing the package.

Maintenance signals and the MIT package boundary

The repository is not archived. Its last push was on 2026-08-30, and the listed releases include version 3.11.0 on 2026-08-30, 3.10.0 on 2026-06-08, and 3.9.0 on 2026-01-19. Those dates indicate recent repository activity within the documented release history. They do not by themselves guarantee that every grammar or integration will change without notice.

The package is marked ESM only, and the `package.json` files list includes `index.js`, `index.d.ts`, `lang/`, `lib/`, `notice`, and `style/`. Consumers should account for ESM requirements and for changes in grammar and style assets when upgrading. The package version in browser examples is pinned to `@wooorm/starry-night@3`, while the npm installation command installs the package without a documented version constraint.

The declared license is MIT. That applies to the package under its MIT terms, but the repository also contains grammar, styles, and a `notice` file. The contents of `notice` are not reproduced in the project description, so it does not support conclusions about separate third-party terms or redistribution details. Teams that redistribute the package or bundle particular grammars should inspect that file and the relevant upstream grammar licenses before treating the MIT declaration as covering every included asset.

For adoption, the concrete first checks are the required grammar list, the size of the selected language set, and the project's own dependency and bundling behavior. Then inspect how the resulting `hast` tree is rendered and which stylesheet is loaded.

Editorial conclusion

Adopt @wooorm/starry-night when GitHub-compatible highlighting, themeable classes, and AST output matter more than bundle size. Do not choose it for a browser that must avoid the WASM and grammar payload without careful budgeting. Before adoption, verify the required language list, inspect the shipped `notice` file, and check whether `common` covers your actual languages.

Frequently asked questions

How do I install @wooorm/starry-night and highlight code?

Install it with `npm install @wooorm/starry-night`, then import `common` and `createStarryNight`, initialize the highlighter, resolve a flag with `flagToScope`, and call `highlight` with the source and scope. The README documents Node.js 16+ for npm installation.

Does @wooorm/starry-night produce HTML or an AST?

It produces a `hast` AST containing root, element, and text nodes. The README explains that the tree can be serialized to HTML or converted into React or Preact nodes.

Is @wooorm/starry-night suitable for browsers?

It can be used in browsers through the documented `esm.sh` import and stylesheet examples, but the README warns that its WASM dependency and large grammars may make it too heavy there. It names `lowlight` and `refractor` as possible alternatives when browser weight is the priority.

What is the difference between common and all grammars in starry-night?

The README describes `common` as roughly 35 languages and `all` as roughly 600 languages. It reports that `common` adds about 250 kB while `all` adds about 1.6 MB, so applications should select the smallest set that covers their requirements.

Does starry-night match GitHub's current highlighter exactly?

The project describes itself as an open source implementation of GitHub's closed-source `PrettyLights`, not GitHub's newer closed-source `TreeLights`. The README also notes small markup differences, so exact current GitHub parity should not be assumed.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. wooorm/starry-night 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/wooorm-starry-night.svg)](https://hysenlabs.com/projects/wooorm-starry-night)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/wooorm-starry-night.svg)](https://hysenlabs.com/projects/wooorm-starry-night)

Was this useful?