Is there a slugify-with-counter helper? How does it work?
Is there a slugify-with-counter helper? How does it work?
About sindresorhus/slugify · 2 messages · last activity 9/1/2026
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]:
- Calls the regular
slugify(string, options)on the input first [index.js:L116]. - If the result is an empty string, it returns
''immediately [index.js:L118-L120]. - It lowercases the slug (
stringLower) to use as a case-insensitive lookup key [index.js:L122]. - 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. - It looks up the current
counterfor the exact lowercased slug in the map [index.js:L124]. - It updates the map: increments the counter if one exists, otherwise sets it to
1[index.js:L125]. - It reads back the just-updated value as
newCounter(defaulting to2if somehow missing) [index.js:L126]. - If
newCounter >= 2(i.e., this slug has been seen before) ornumberless > 2, it appends-${newCounter}to the slug to disambiguate it [index.js:L127-L129]. - 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 > 2for suffixing logic? - How does regular
slugify()handle options merging? - What does
preserveTrailingDashoption actually do?
Want to ask your own question?
Open chat for sindresorhus/slugifyPublic Q&A. Generated by RepoPilot from the actual source of sindresorhus/slugify. AI answers can be incomplete or stale — verify before relying on them.