RepoPilot

Is there a slugify-with-counter helper? How does it work?

About sindresorhus/slugify · 2 messages · last activity 9/1/2026

Question

Is there a slugify-with-counter helper? How does it work?

RepoPilot

Yes — it's slugifyWithCounter(), exported from the main module.

How it works:

slugifyWithCounter() is a factory function that returns a stateful slugify function ("countable") backed by a closure over a Map called occurrences [index.js:L112-L113].

The returned countable function [index.js:L115-L128]:

  1. Calls the regular slugify(string, options) on the input first [index.js:L116].
  2. If the result is an empty string, it returns '' immediately [index.js:L118-L120].
  3. It lowercases the slug (stringLower) to use as a case-insensitive lookup key [index.js:L122].
  4. It computes numberless — the recorded occurrence count for the "base" version of the slug, obtained by stripping any trailing -<number> suffix(es) via the regex /(?:-\d+?)+?$/ [index.js:L123]. This helps detect cases where a numbered variant already collided with future occurrences.
  5. It looks up the current counter for the exact lowercased slug in the map [index.js:L124].
  6. It updates the map: increments the counter if one exists, otherwise sets it to 1 [index.js:L125].
  7. It reads back the just-updated value as newCounter (defaulting to 2 if somehow missing) [index.js:L126].
  8. If newCounter >= 2 (i.e., this slug has been seen before) or numberless > 2, it appends -${newCounter} to the slug to disambiguate it [index.js:L127-L129].
  9. Returns the (possibly suffixed) string [index.js:L131].

It also exposes a .reset() method that clears the occurrences map, letting you start counting fresh (e.g., between documents) [index.js:L133-L135].

Example usage from the docs: calling it repeatedly with 'foo bar' yields 'foo-bar', then 'foo-bar-2', and after .reset() it goes back to 'foo-bar' [readme.md:L245-L266, index.d.ts:L220-L263].

The TypeScript type for the returned function is CountableSlugify, which documents both the callable signature (string, options?) => string and the reset(): void method, plus a use-case example: generating unique HTML ids for repeated section headings (e.g., multiple "Example" subsections) so anchors resolve correctly [index.d.ts:L220-L280].

FOLLOW_UPS:

  • Why check numberless > 2 for suffixing logic?
  • How does regular slugify() handle options merging?
  • What does preserveTrailingDash option actually do?

Want to ask your own question?

Open chat for sindresorhus/slugify

Public Q&A. Generated by RepoPilot from the actual source of sindresorhus/slugify. AI answers can be incomplete or stale — verify before relying on them.