← BACK TO ENGINEERING
Runtime 6 min read

Getting Started with Glow: Read Markdown in Your Terminal

Most projects keep their most useful knowledge in Markdown: the README, the changelog, a docs/ folder. Most of us read those files either raw in a terminal, all ## and |---|, or by switching to a browser.

@oakoliver/glow renders Markdown right in the terminal: headings, tables, code with syntax highlighting, task lists. It's a TypeScript port of Charm's Glow. This guide covers the CLI from first install to two small workflow tricks.

glow FILE renders a document; glow DIR opens a browser where you can search, read and scroll every Markdown file in it.

glow docs: the file browser lists four documents, typing /dep filters them to deploying.md, Enter opens it in the pager, and d scrolls through an environment table, a systemd unit with highlighting and a troubleshooting list

Every command below was run as written, against a small made-up project called pixel-bot, and every picture is a recording of that run. How the port itself was built is covered in Porting Go's Glow to TypeScript.


I – Install

npm install -g @oakoliver/glow

That puts a glow command on your PATH. It runs on Node, and its rendering comes from @oakoliver/glamour, which in turn pulls in highlight.js for code blocks and node-emoji for :emoji: shortcodes.

If you'd rather not install anything globally, add it to a project instead (npm install @oakoliver/glow); section VI shows how to use it from there.


II – Render a File

glow README.md

glow README.md: the pixel-bot README with a highlighted title, a blockquote, an install code block, a three-row command table with column dividers, and a task list with two checked items

Everything you'd expect from a Markdown viewer is there: the table gets real columns, code spans are highlighted, the blockquote gets a bar, and - [x] becomes a checkbox. The output is plain text with colour codes, so you can scroll back through it like any other command output.

Glow also reads from places other than local files:

glow https://raw.githubusercontent.com/charmbracelet/glow/master/README.md
glow github://charmbracelet/glow

The second form finds a repository's README for you; gitlab://owner/repo works the same way.


III – Read From stdin

Pass - as the file and glow renders whatever is piped into it:

cat CHANGELOG.md | glow -
echo 'Deploy **done** in `42s`' | glow -

cat CHANGELOG.md | glow - renders a two-release changelog with bullet lists; echo 'Deploy done in 42s' | glow - renders a single line with bold text and a code span

That makes glow a finishing step for anything that produces Markdown: release notes from a script, a generated report, an LLM's answer.

There's one behaviour to know about, and it comes straight from the original Glow. Whenever glow's stdin is not a terminal, glow reads stdin, even if you also give it a file name. In an interactive shell that never matters. In a script, a cron job, a CI step or some git hooks, stdin may be an open pipe, and then glow README.md waits for input that never comes. The fix is to give it an empty stdin:

glow README.md < /dev/null

The git hook in section VI does exactly that.


IV – Browse a Folder

Point glow at a directory (or run it with no arguments for the current one) and it opens a full-screen browser instead of printing:

glow docs

The animation at the top of this article is that command. The keys you'll use most:

Key Does
↑ ↓ / j k Move through the list, or scroll a document
/ Filter the list by name
Enter Open the selected document
d / u, f / b Half page or full page down / up
g / G Top / bottom of the document
Esc Back to the list
e Open the document in $EDITOR
c Copy the document to the clipboard
? Show every key
q Quit

To open a single file in the reader instead of printing it, use glow -t README.md; add -l for line numbers.


V – Styles and Width

Two flags control most of how the output looks. -s picks a style and -w sets the wrap width:

glow -s dracula -w 60 docs/getting-started.md
glow -s pink -w 60 docs/getting-started.md

The same getting-started page rendered twice at 60 columns: first with the dracula style (purple heading, highlighted shell command), then with the pink style (pink heading, code span with a background)

The built-in styles are auto (the default), dark, light, pink, dracula, tokyo-night and notty. notty has no colours, and glow switches to it automatically when its output isn't a terminal, so glow README.md > out.txt writes plain, uncoloured text. You can also pass the path of a JSON style file to -s.

To make a choice permanent, run:

glow config

It opens glow's config file in $EDITOR, creating it first with the default for every option and a comment explaining each one. On macOS the file lives at ~/Library/Preferences/glow/glow.yml. Two lines are enough to change the defaults:

style: "dracula"
width: 80

VI – Glow in Your Workflow

Show the changelog after every pull

Git runs .git/hooks/post-merge after every merge, including the one inside git pull. This hook checks whether the merge changed CHANGELOG.md, and if it did, renders it:

#!/bin/sh
# .git/hooks/post-merge: show the changelog when a merge or pull changed it.
if git diff --name-only ORIG_HEAD HEAD | grep -qx CHANGELOG.md; then
  glow -w 70 CHANGELOG.md < /dev/null
fi

Save it as .git/hooks/post-merge, make it executable with chmod +x, and the next pull that brings a release note shows it:

cat .git/hooks/post-merge shows the hook; git merge release fast-forwards one commit that changed CHANGELOG.md, and the hook renders the new changelog with the 0.3.0 entry at the top

ORIG_HEAD is where your branch was before the merge, so the git diff lists exactly the files the merge brought in. The < /dev/null is the stdin precaution from section III.

Give your project a docs command

If glow is a dependency of your project rather than a global install, npm scripts can still call it, because npm run puts node_modules/.bin on the PATH:

npm install @oakoliver/glow
npm pkg set scripts.docs="glow docs"
npm run docs

Anyone who clones the repository now gets the docs browser with one command, without installing anything globally.

From TypeScript

glow also exports the pieces of its TUI. For example, this opens the same browser from your own program:

// open-docs.ts
import { NewProgram, defaultConfig } from '@oakoliver/glow';

const cfg = defaultConfig();
cfg.path = './docs';

await NewProgram(cfg).run();

I ran it with both node open-docs.ts and bun open-docs.ts. If what you want is the rendering rather than the browser, Markdown in and styled text out inside your own code, use @oakoliver/glamour directly: it's the library glow uses to draw every page above.


VII – What to Read Next

  • Line numbers and long files. glow -t -l opens a file in the reader with line numbers, which is the most comfortable way to read a long design doc in a terminal.
  • Hidden folders. By default the browser skips hidden and ignored files; -a shows everything.
  • Your own style. Download one of the JSON styles from upstream Glamour's styles/ folder, change the colours or prefixes, and pass the file to -s.
  • The rest of the toolkit. glow is built from @oakoliver/bubbletea, @oakoliver/bubbles, @oakoliver/lipgloss and glamour. Each one is usable on its own if you want to build a terminal app of your own.

Requirements

  • Runtime. The glow command runs on Node; everything here was run on Node 26.8. The library import in section VI also ran on Bun 1.4. The package ships ES module and CommonJS builds with type declarations.
  • Terminal. Any terminal with ANSI colour. The browser and reader need an interactive terminal; for scripts, print with glow FILE and remember < /dev/null.
  • Source. github.com/oakoliver/glow, MIT licensed, based on charmbracelet/glow.
"Simplicity is the ultimate sophistication."