npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react.vim

v1.0.1

Published

A vim-like lightweight code editor React component powered by Shiki

Readme


Drop-in <Vim /> component with real Vim modes, Shiki highlighting, and zero configuration.

Features

  • Real Vim Engine — Not a wrapper around another editor. A purpose-built Vim engine in TypeScript with Normal, Insert, Visual, Visual-Line, Visual-Block, and Command-line modes.
  • Shiki Highlighting — 60+ themes, 200+ languages. Same engine powering VS Code syntax highlighting.
  • Comprehensive Keybindings — Motions, operators, counts, text objects, registers, macros, marks, dot-repeat, search, substitute, indent, and more. See Supported Vim Features below.
  • Read-Only ModereadOnly prop for a zero-config code viewer with full navigation & search capabilities.
  • Tiny & Focused — No heavy deps. React + Shiki. Tree-shakeable ESM, full TypeScript types.
  • Callback-drivenonSave, onYank, onChange, onModeChange, onAction — you control what happens.
  • Customizable — CSS variables for font, colors, cursor, selection, gutter, and status line. Shiki options are passed through transparently.
  • Extensible — Internal hooks (useVimEngine, useShikiTokens) and core modules are exported for custom integrations.

Install

npm install react.vim shiki react react-dom

shiki, react, and react-dom are peer dependencies.

Quick Start

import Vim from "react.vim";
import "react.vim/styles.css";
import { createHighlighter } from "shiki";

const highlighter = await createHighlighter({
  themes: ["vitesse-dark"],
  langs: ["typescript"],
});

function App() {
  return (
    <Vim
      content={`function greet(name: string) {\n  return "Hello, " + name;\n}`}
      highlighter={highlighter}
      lang="typescript"
      theme="vitesse-dark"
      onSave={(content) => {
        fetch("/api/save", { method: "POST", body: content });
      }}
      onYank={(text) => {
        navigator.clipboard.writeText(text);
      }}
    />
  );
}

Try it live in the playground

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | content | string | required | The code to display and edit | | highlighter | HighlighterCore | required | Shiki highlighter instance | | lang | string | required | Language for syntax highlighting | | theme | string | required | Shiki theme name | | shikiOptions | Record<string, unknown> | — | Additional options passed to Shiki's codeToTokens | | cursorPosition | string | "1:1" | Initial cursor position ("line:col", 1-based) | | readOnly | boolean | false | Disable editing (motions still work) | | autoFocus | boolean | false | Focus the editor on mount | | indentStyle | "space" \| "tab" | "space" | Use spaces or tabs for indentation | | indentWidth | number | 2 | Number of spaces (or tab width) per indent level | | showLineNumbers | boolean | true | Show line number gutter | | className | string | — | Additional class for the container |

Callbacks

| Prop | Signature | Trigger | |------|-----------|---------| | onChange | (content: string) => void | Any content edit | | onYank | (text: string) => void | Text yanked (yy, dw, etc.) | | onSave | (content: string) => void | :w command | | onModeChange | (mode: VimMode) => void | Mode transition | | onAction | (action: VimAction, key: string) => void | Every vim engine action (for debugging / logging) |

Supported Vim Features

Modes

| Key | Action | |-----|--------| | i a I A | Enter insert mode (before/after cursor, line start/end) | | o O | Open line below / above and enter insert mode | | v | Visual mode (character-wise) | | V | Visual line mode | | Ctrl-V | Visual block mode (rectangular selection) | | Escape | Return to normal mode |

Motions

| Key | Action | |-----|--------| | h j k l | Left / Down / Up / Right | | w e b | Next word / End of word / Previous word | | W B | Next WORD / Previous WORD (whitespace-delimited) | | 0 ^ $ | Line start / First non-blank / Line end | | gg G | File start / File end (or {count}gg, {count}G) | | H M L | Top / Middle / Bottom of visible screen | | f{char} F{char} | Find char forward / backward | | t{char} T{char} | Till char forward / backward | | ; , | Repeat last f/F/t/T forward / backward | | % | Jump to matching bracket | | {count}{motion} | Repeat motion (e.g. 5j, 3w, 10G) |

Operators

| Key | Action | |-----|--------| | d{motion} | Delete | | y{motion} | Yank (copy) | | c{motion} | Change (delete + enter insert) | | >{motion} | Indent | | <{motion} | Dedent | | dd yy cc >> << | Operate on whole line | | D C | Delete / Change to end of line | | {count}{operator}{motion} | e.g. 3dw, 2yy, 5>> |

Text Objects

Work with operators (ciw, da") and visual mode (viw, va().

| Key | Description | |-----|-------------| | iw / aw | Inner / a word | | iW / aW | Inner / a WORD (whitespace-delimited) | | i" / a" | Inner / a double-quoted string | | i' / a' | Inner / a single-quoted string | | i` / a` | Inner / a backtick-quoted string | | i( / a( | Inner / a parentheses (also i) / a)) | | i{ / a{ | Inner / a braces (also i} / a}) | | i[ / a[ | Inner / a brackets (also i] / a]) | | i< / a< | Inner / a angle brackets (also i> / a>) |

Editing

| Key | Action | |-----|--------| | x | Delete character under cursor | | r{char} | Replace character under cursor | | ~ | Toggle case and advance cursor | | p / P | Paste after / before cursor | | J | Join current line with next | | u / Ctrl-R | Undo / Redo | | . | Repeat last change |

Registers

| Key | Action | |-----|--------| | "ayy | Yank line into register a | | "ap | Paste from register a | | "a-"z | 26 named registers, persist across operations | | "" | Unnamed register (default) |

Macros

| Key | Action | |-----|--------| | qa | Start recording macro into register a | | q | Stop recording | | @a | Play macro from register a | | @@ | Repeat last played macro |

Marks

| Key | Action | |-----|--------| | ma | Set mark a at current cursor position | | `a | Jump to exact position of mark a | | 'a | Jump to line of mark a | | a-z | 26 local marks available |

Search & Commands

| Key | Action | |-----|--------| | /{pattern} | Search forward (regex) | | ?{pattern} | Search backward (regex) | | n / N | Next / Previous match | | * / # | Search word under cursor forward / backward | | Ctrl-U / Ctrl-D | Half page up / down | | Ctrl-B / Ctrl-F | Full page up / down | | :w | Save | | :{number} | Go to line | | :s/old/new/g | Substitute (current line, % for all lines) | | :set number / :set nonumber | Toggle line numbers |

Visual Block Mode

| Key | Action | |-----|--------| | Ctrl-V | Enter visual block mode | | I | Insert at left edge of block (replicated on Escape) | | A | Append at right edge of block (replicated on Escape) | | d y c | Operate on rectangular selection | | o | Swap anchor and cursor |

Status Line

The status line automatically shows contextual information:

  • Mode indicator (-- INSERT --, -- VISUAL --, -- VISUAL BLOCK --, etc.)
  • Operation feedback (6 lines yanked, 3 fewer lines, 4 more lines)
  • Register info (6 lines yanked into "a)
  • Macro recording indicator (recording @a)
  • Search / command input (:, /, ?)
  • Cursor position (line:col)

Styling

Override CSS variables for visual customization. Behavioral settings like indentation are controlled via props.

.sv-container {
  --sv-font-family: "JetBrains Mono", monospace;
  --sv-font-size: 14px;
  --sv-line-height: 1.5;
  --sv-cursor-color: rgba(255, 255, 255, 0.6);
  --sv-selection-bg: rgba(100, 150, 255, 0.3);
  --sv-gutter-color: #858585;
  --sv-statusline-bg: #252526;
  --sv-statusline-fg: #cccccc;
  --sv-focus-color: #007acc;
}

Tab display width is controlled by the indentWidth prop, not CSS.

Hooks

For advanced use cases, the internal hooks are exported:

import { useVimEngine, useShikiTokens } from "react.vim";

const engine = useVimEngine({
  content: "hello world",
  onSave: (content) => { /* ... */ },
});

// engine.cursor, engine.mode, engine.handleKeyDown, etc.

Contributing

Setup

bun install
bun run dev         # Watch mode (builds the library)
bun run test        # Run tests
bun run typecheck   # Type check
bun run lint        # oxlint
bun run fmt         # oxfmt

Debug App

A local debug app lives in debug/. It references the react.vim source directly via Vite aliases — no build step needed for live feedback.

cd debug
bun install
bun run dev

The debug app includes:

  • Theme / Language selectors — switch Shiki themes and languages on the fly
  • Indent controls — toggle between Tab and Space indentation with width selection
  • CSS variable controls — adjust colors, font size, line height, etc. with color pickers and sliders
  • Operation history — every vim action is logged with the triggering key (<cursor-move> [j] -> 5:1)
  • "Copy for LLM" button — copies the full operation log, editor state, and settings as a markdown report you can paste directly into a bug report or LLM conversation

Changes to files under src/ are reflected immediately in the debug app via HMR.

Workflow

  1. Start the debug app (cd debug && bun run dev)
  2. Make changes to the library source in src/
  3. Test your changes interactively in the browser
  4. Use "Copy for LLM" in the history panel if you need to report unexpected behavior
  5. Run bun run test before submitting a PR

PRs are welcome! Please make sure bun run test and bun run typecheck pass before submitting.

License

MIT © JJ