slugify

Slugify a string with accurate Unicode transliteration for URLs, filenames, and IDs.

Library
npm
v3.0.1
2,709 stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
61 /100 Good
Development Activity 52
Maintenance 44
Community 48
Maturity 60
Momentum 40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
75 /100 Good
Architecture 78
Code Quality 90
Innovation 68
Learning Curve 65

@sindresorhus/slugify turns any string into a lowercase, URL- and filename-safe slug. It transliterates non-Latin scripts — German umlauts, Vietnamese, Arabic, Russian, and more — into ASCII equivalents, decamelizes identifiers while correctly preserving acronyms and plural forms, and strips the apostrophe from contractions and possessives so Conway's Law becomes conways-law rather than conway-s-law.

Beyond the default export, the package includes slugifyWithCounter(), a stateful helper that appends numeric suffixes to repeated slugs — useful for generating collision-free heading anchors across a single document. The library is intentionally small and dependency-light, ships as ESM-only for Node 20+, and exposes granular options for separator characters, casing, custom replacements, locale-specific transliteration, and character preservation.

What You Get

  • A default slugify(string, options?) export with sensible defaults (lowercase, decamelize, and transliterate all enabled)
  • A slugifyWithCounter() factory that tracks previously seen slugs and appends numeric suffixes to duplicates
  • Built-in transliteration for German, Vietnamese, Arabic, Russian, and other major scripts via @sindresorhus/transliterate
  • Options for separator character, casing, custom replacements, locale, and character preservation
  • Full TypeScript type definitions (index.d.ts) with documented options and inline usage examples

Common Use Cases

  • Generating SEO-friendly URL slugs from post or page titles
  • Producing unique anchor IDs for markdown or HTML headings
  • Sanitizing user-uploaded filenames before writing them to disk or object storage
  • Normalizing multilingual text into a consistent ASCII key for search indexes and deduplication

Under The Hood

Architecture

The package is a single-file, purely functional ESM module (index.js) with no classes or internal state beyond the closure inside slugifyWithCounter(). The default slugify export runs a fixed pipeline — custom-replacement substitution via @sindresorhus/transliterate, decamelize, a dynamically built buildPatternSlug regex driven by the active options, contraction stripping, and finally removeMootSeparators — with small dedicated helpers for each stage rather than one monolithic function. slugifyWithCounter() wraps the same pipeline in a Map-backed closure that tracks prior slugs and appends a numeric suffix on repeats. Because every stage is a pure function taking explicit options, the abstraction that would break first is buildPatternSlug, since every option (preserveCharacters, transliterate, casing) folds into the single negation-set regex it builds.

Tech Stack

The project is ESM-only ("type": "module"), targets Node.js 20+, and ships hand-written TypeScript declarations (index.d.ts) alongside plain JavaScript source rather than compiling from TypeScript. Runtime dependencies are minimal and both from the same author’s ecosystem: @sindresorhus/transliterate for Unicode-to-ASCII conversion and escape-string-regexp for safely embedding a user-supplied separator into dynamically constructed regexes. Testing runs on ava, linting on xo (the author’s opinionated ESLint preset), and there is no separate build step — the files field ships index.js, index.d.ts, and overridable-replacements.js directly.

Code Quality

The test suite is extensive relative to the module’s size, covering basic slugification, camelCase/acronym decamelization (including plural-acronym edge cases like APIs vs APISection), contraction and possessive handling across straight and curly apostrophes, emoji and symbol replacement, and locale-specific transliteration. Errors are handled explicitly — a non-string argument throws a TypeError with a descriptive message rather than failing silently. Source comments call out specific regex-performance decisions (avoiding quadratic backtracking on repeated-character runs), indicating deliberate attention to correctness under adversarial input. CI runs the test suite via GitHub Actions across a Node.js version matrix (20 and 24) on every push and pull request.

What Makes It Unique

Most slugify implementations treat decamelization and transliteration as simple find-and-replace passes; this one instead builds its character-matching regex dynamically per call based on the active options and handles compound edge cases explicitly — plural acronyms staying intact through camelCase splitting, contractions dropping only their word-final apostrophe without disturbing unrelated ones, and a counter-aware variant purpose-built for generating collision-free anchors. The engineering is a careful refinement of a well-understood problem rather than a novel algorithmic approach, but the attention to edge-case correctness and regex-performance safety distinguishes it from more naive slugify libraries in the same ecosystem.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers