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

classname-search

v1.1.0

Published

A CLI tool that searches inside class and className attributes and matches against each class name individually.

Readme

classname-search

A CLI tool that searches inside class and className attributes and matches against each class name individually.

[!IMPORTANT]
This documentation is written primarily for AI agents.
No extra context is provided for human readers.

Why not grep?

  1. Matches each class name individually

    • grep matches entire lines. This tool parses class and className attributes and matches against each class name separately, so you can target flex-col without matching flex.
  2. Structured output (JSONLines)

    • The search command outputs results as JSONLines with file path, line number, matched string, and full class value—easy to parse and act on programmatically.
  3. Regex replacement with capture groups

    • The replace command supports $1, $2, etc., enabling pattern-based bulk replacements (e.g., -slate-(\d+)$-neutral-$1) in a single command.

Subcommands

| Subcommand | Description | | ---------- | ---------------------------------- | | stats | Count matched class names per file | | search | Search for class names | | replace | Search and replace class names | | remove | Remove class names |

Usage

npx classname-search <subcommand> '<target-glob>' '<class-regex>' [options]

| Argument | Description | | --------------- | ---------------------------------------------------------------------------------- | | <target-glob> | Glob pattern for target files (fast-glob syntax). Quote to prevent shell expansion | | <class-regex> | Regex pattern to match classes. Quote to prevent shell interpretation |

⚠️ Windows CLI Issues

PowerShell environment: To check if you're in PowerShell, run echo --%. If the output is empty, you're in PowerShell. Always use the stop-parsing token (--%) with quotes. Without it, special characters may be silently modified, causing incorrect results without any error. The tool automatically strips outer quotes from arguments.

cmd.exe passthrough (when not using --%): npx may pass through cmd.exe internally on Windows, which consumes ^ as an escape character. To check if ^ is consumed, run npx -y node -e "console.log(process.argv[1])" "^test". If the output is test instead of ^test, escape ^ as ^^ (e.g., ^flex$^^flex$). Without this, results will be silently incorrect.

Limitations

  • Supported attributes: Only class and className attributes are targeted.
  • One attribute per line: If a line contains multiple class or className attributes, only the first one is processed.
  • Multi-line attributes are ignored: Class attributes spanning multiple lines are not matched.
  • Dynamic values are ignored: Only values enclosed in double or single quotes are targeted. Attributes using JSX expressions (e.g., className={...}) are skipped.
  • Comments are processed: The tool does not ignore commented-out code.
  • Files outside CWD are ignored: The tool only processes files within the current working directory.

stats

Count matched class names per file.

npx classname-search stats '<target-glob>' '<class-regex>' [options]

Arguments/Options:

| Argument/Option | Description | | --------------- | ---------------------------------------------------- | | -v, --verbose | Show detailed output including files with no matches |

Examples

Command:

npx classname-search stats 'src/components/**/*.jsx' 'text-'

Output:

src/components/Header.jsx: 3 matches
src/components/Main.jsx: 4 matches

Total: 7 matches in 2 files

Command:

npx classname-search stats --verbose 'src/components/**/*.jsx' 'text-'

Output:

src/components/Header.jsx: 3 matches
src/components/Main.jsx: 4 matches
src/components/Footer.jsx: 0 matches

Total: 7 matches in 3 files

search

Search for class names.

npx classname-search search '<target-glob>' '<class-regex>' [options]

Arguments/Options:

| Argument/Option | Description | | --------------------- | ---------------------------------- | | -o, --output <file> | Output results to a JSONLines file |

Output Format (JSONLines):

Each match is output as a JSON object on a separate line.

| Field | Description | | ------------ | ---------------------------------------------------------- | | file | File path relative to the current working directory | | line | Line number (1-based) | | matched | The substring that matched the regex pattern | | className | The individual class name that contains the matched string | | classValue | Full value of the class attribute |

Examples

Command:

npx classname-search search 'src/components/**/*.jsx' 'flex'

Output:

{"file":"src/components/Header.jsx","line":3,"matched":"flex","className":"flex","classValue":"flex flex-col"}
{"file":"src/components/Header.jsx","line":3,"matched":"flex","className":"flex-col","classValue":"flex flex-col"}
{"file":"src/components/Header.jsx","line":4,"matched":"flex","className":"inline-flex","classValue":"inline-flex"}

Command:

npx classname-search search 'src/components/**/*.jsx' '^flex$'

Output:

{"file":"src/components/Header.jsx","line":3,"matched":"flex","className":"flex","classValue":"flex flex-col"}

Command:

npx classname-search search 'src/components/**/*.jsx' '^items-.+'

Output:

{"file":"src/components/Header.jsx","line":5,"matched":"items-start","className":"items-start","classValue":"items-start"}

Command:

npx classname-search search 'src/components/**/*.jsx' '^gap-\[.+\]$'

Output:

{"file":"src/components/Header.jsx","line":6,"matched":"gap-[10px]","className":"gap-[10px]","classValue":"gap-[10px]"}

replace

Search and replace class names.

npx classname-search replace '<target-glob>' '<class-regex>' '<replacement>' [options]

Arguments/Options:

| Argument/Option | Description | | --------------- | -------------------------------------- | | <replacement> | String to replace matched classes with |

[!NOTE]
When a class is replaced with an empty string, extra whitespace is automatically removed to maintain single-space separation.

Output:

Replaced <N> matches of "<class-regex>" with "<replacement>" in <N> files.

Examples

Basic usage:

# Replace "fixed" with "absolute"
npx classname-search replace 'src/components/**/*.jsx' '^fixed$' 'absolute'
# Replace "-red-100" with "-red-200"
npx classname-search replace 'src/components/**/*.jsx' -- '-red-100$' '-red-200'

Using capture groups:

# Migrate slate colors to neutral
# ✅ bg-slate-100 → bg-neutral-100
# ✅ text-slate-200 → text-neutral-200
# ❌ bg-slate-300/50
npx classname-search replace 'src/components/**/*.jsx' -- '-slate-(\d+)$' '-neutral-$1'

# Migrate blue colors to indigo
# ✅ text-blue-400 → text-indigo-400
# ✅ bg-blue-500 → bg-indigo-500
# ❌ border-blue-600
npx classname-search replace 'src/components/**/*.jsx' '(text|bg)-blue-(\d+)$' '$1-indigo-$2'

# Change breakpoint md: to lg:
# ✅ md:hidden → lg:hidden
# ✅ md:flex → lg:flex
# ❌ sm:hidden
npx classname-search replace 'src/components/**/*.jsx' '^md:(.+)$' 'lg:$1'

remove

Remove class names.

npx classname-search remove '<target-glob>' '<class-regex>'

[!NOTE] This command is equivalent to replace '<target-glob>' '<class-regex>' ''. Extra whitespace is automatically removed to maintain single-space separation.

Output:

Removed <N> matches of "<class-regex>" in <N> files.

Examples

# Remove "font-normal" class
npx classname-search remove 'src/components/**/*.jsx' '^font-normal$'

# Remove all slate color classes
npx classname-search remove 'src/components/**/*.jsx' -- '-slate-\d+'