slug vs slugify vs url-slug
Generating URL-Safe Slugs in JavaScript Applications
slugslugifyurl-slugSimilar Packages:

Generating URL-Safe Slugs in JavaScript Applications

slug, slugify, and url-slug are npm packages designed to convert human-readable text into URL-safe, SEO-friendly slugs by replacing spaces, special characters, and diacritics with standardized ASCII equivalents. These utilities are commonly used when generating clean URLs for blog posts, product pages, or user profiles from titles or names. While they share a common goal, they differ significantly in configurability, character handling, maintenance status, and API design.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
slug039533.2 kB64 months agoMIT
slugify01,72820.9 kB443 years agoMIT
url-slug09134 kB13 years agoMIT

Generating Clean URLs: slug vs slugify vs url-slug

When building web apps, you often need to turn titles like "My Café’s 2024 Résumé" into clean URLs like my-cafes-2024-resume. Three popular npm packages claim to solve this: slug, slugify, and url-slug. But they’re not equally reliable or flexible. Let’s break down what each actually does — and which one deserves a spot in your codebase.

⚠️ Deprecation Status: One Package Is Dead

Before comparing features, check maintenance status. url-slug is officially deprecated. Its npm page states: "This package has been deprecated", and its GitHub repo is archived. Do not use it in new projects. That leaves us with slug and slugify as viable options.

🔤 Basic Usage: How Simple Is "Hello World"?

All three aim for one-liner convenience, but their defaults differ.

slug requires importing the function and calling it directly:

import slug from 'slug';

const result = slug("My Café’s 2024 Résumé");
// Output: "my-cafes-2024-resume"

slugify works similarly but uses a different export style:

import slugify from 'slugify';

const result = slugify("My Café’s 2024 Résumé");
// Output: "my-cafes-2024-resume"

url-slug (deprecated):

// Do not use — shown for completeness only
import { urlSlug } from 'url-slug';

const result = urlSlug("My Café’s 2024 Résumé");
// Output: "my-cafes-2024-resume"

At first glance, they seem interchangeable. But dig deeper, and the differences matter.

🌍 Handling Non-English Characters: Beyond Basic Latin

What happens with Cyrillic, Greek, or Vietnamese text? This is where slug pulls ahead.

slug supports custom character sets and even includes built-in locales. You can load Russian, German, or Arabic transliteration rules:

import slug from 'slug';
slug.charmap = slug.charmap; // default is 'en'

// Use built-in German rules
slug("Müllerstraße", { charmap: slug.charmap.de });
// Output: "muellerstrasse"

// Or define your own
slug("αβγ", { charmap: { 'α': 'a', 'β': 'b', 'γ': 'g' } });
// Output: "abg"

slugify uses a fixed internal character map based on Unicode normalization. It handles common accented Latin characters well but doesn’t support custom mappings or locales:

import slugify from 'slugify';

slugify("Müllerstraße");
// Output: "mullerstrasse" (works for common cases)

slugify("αβγ");
// Output: "" (Greek letters become empty — not ideal)

url-slug had limited Unicode support and is no longer updated — avoid entirely.

⚙️ Configuration: Control Over Output Format

Need uppercase slugs? Custom separators? slug gives you knobs; slugify gives you on/off.

slug offers granular options:

import slug from 'slug';

slug("Hello World!", {
  lower: false,        // keep case → "Hello-World"
  replacement: '_',    // use underscore → "hello_world"
  symbols: true        // remove symbols like ! → "hello-world"
});

It also supports RFC3986 mode for strict URL compliance:

slug("Hello World!", { mode: 'rfc3986' });
// Output: "hello-world" (removes ! per RFC spec)

slugify provides only two options:

import slugify from 'slugify';

slugify("Hello World!", {
  lower: true,         // force lowercase
  strict: true         // remove non-alphanumeric chars (except spaces/hyphens)
});
// With strict: true → "hello-world"
// Without → "hello-world!" (exclamation remains!)

No control over separator, case preservation, or symbol handling beyond strict.

🧪 Edge Cases: Numbers, Emojis, and Empty Strings

How do they handle tricky inputs?

  • Numbers: Both slug and slugify preserve digits ("2024""2024").
  • Emojis: slug removes them by default; slugify keeps them unless strict: true is set.
  • Empty input: Both return empty strings.
  • Multiple spaces: Both collapse to single separator.

But slug lets you override emoji behavior via custom charmaps; slugify does not.

📦 Bundle Impact and Dependencies

While bundle size isn't our focus here, note that slug is slightly heavier due to its extensibility, while slugify is minimal. However, for most apps, the difference is negligible compared to the value of correct internationalization.

🛠️ Real-World Recommendation

Use slug when:

  • Your app supports multiple languages
  • You need to customize how specific characters are replaced
  • You require strict URL compliance (RFC3986)
  • You want future-proof, actively maintained code

Use slugify when:

  • You only handle basic English content
  • You prefer "it just works" with zero config
  • Your project is small and unlikely to need i18n

Never use url-slug — it’s deprecated and unmaintained.

💡 Final Thought

Slugs seem simple until you hit "José María" or "北京". If your app might ever touch non-English text, slug’s configurability pays off fast. For throwaway prototypes or internal tools with English-only data, slugify gets the job done with less overhead. But always avoid deprecated packages — they’re technical debt waiting to happen.

How to Choose: slug vs slugify vs url-slug

  • slug:

    Choose slug if you need fine-grained control over transliteration rules, support for multiple locales, or the ability to extend character mappings. It offers a robust, well-maintained API with options for custom replacements, lowercasing behavior, and mode selection (e.g., RFC3986 compliance). Ideal for internationalized applications where precise control over non-Latin characters is required.

  • slugify:

    Choose slugify if you want a simple, zero-configuration solution that works out of the box for basic English text. It automatically handles common cases like trimming whitespace and converting to lowercase, but lacks advanced options for custom character sets or locale-specific transliteration. Best suited for lightweight projects where simplicity trumps configurability.

  • url-slug:

    Do not choose url-slug for new projects — it is deprecated and no longer maintained. The package has been marked as deprecated on npm, and its GitHub repository is archived. Using it introduces technical debt and potential security or compatibility issues. Evaluate slug or slugify instead.

README for slug

slug

Slugifies strings, even when they contain Unicode.

Make strings URL-safe.

  • Respects RFC 3986
  • No dependencies
  • Works in the browser or in Node.js
npm install slug

If you are using TypeScript you can install the accompanying types

npm install --save-dev @types/slug

Example

import slug from 'slug'
var print = console.log.bind(console, '>')

print(slug('i love unicode'))
// > i-love-unicode

print(slug('i love unicode', '_')) // If you prefer something else than `-` as separator
// > i_love_unicode

slug.charmap['♥'] = 'freaking love' // change default charmap or use option {charmap:{…}} as 2. argument
print(slug('I ♥ UNICODE'))
// > i-freaking-love-unicode

// To reset modifications to slug.charmap, use slug.reset():
slug.reset()
print(slug('I ♥ UNICODE'))
// > i-unicode

print(slug('Telephone-Number')) // lower case by default
// > telephone-number

print(slug('Telephone-Number', {lower: false})) // If you want to preserve case
// > Telephone-Number

// We try to provide sensible defaults.
// So Cyrillic text will be transliterated as if it were Russian:
print(slug('маленький подъезд'))
// > malenkij-poduezd

// But maybe you know it's Bulgarian:
print(slug('маленький подъезд', { locale: 'bg' }))
// > malenykiy-podaezd

// To set the default locale:
slug.setLocale('bg')
print(slug('маленький подъезд'))
// > malenykiy-podaezd

print(slug('unicode is ☢'))
// > unicode-is

slug.extend({'☢': 'radioactive'})
print(slug('unicode ♥ is ☢'))
// > unicode-is-radioactive

// slug.extend() modifies the default charmap for the entire process.
// If you need to reset charmap, multicharmap, and the default locale, use slug.reset():

slug.reset()
print(slug('unicode ♥ is ☢'))
// > unicode-is

// Custom removal of characters from resulting slug. Let's say that we want to
// remove all numbers for some reason.
print(slug('one 1 two 2 three 3'))
// > one-1-two-2-three-3
print(slug('one 1 two 2 three 3', { remove: /[0-9]/g }))
// > one-two-three

options

// options is either object or replacement (sets options.replacement)
slug('string', [{options} || 'replacement']);
slug.defaults.mode ='pretty';
slug.defaults.modes['rfc3986'] = {
    replacement: '-',      // replace spaces with replacement
    remove: null,          // (optional) regex to remove characters
    lower: true,           // result in lower case
    charmap: slug.charmap, // replace special characters
    multicharmap: slug.multicharmap, // replace multiple code unit characters
    trim: true,             // trim leading and trailing replacement chars
    fallback: true          // use base64 to generate slug for empty results
};
slug.defaults.modes['pretty'] = {
    replacement: '-',
    remove: null,
    lower: true,
    charmap: slug.charmap,
    multicharmap: slug.multicharmap,
    trim: true,
    fallback: true
};

Differences between slug and slugify packages

Here are some key differences between this package and slugify.

  • Stability: slug is ESM-only.
    slugify supports CommonJS and ESM.
  • Defaults: slug has the lower option enabled by default, lowercasing all slugs ('On SALE' becomes 'on-sale').
    slugify has the lower option disabled by default ('On SALE' becomes 'On-SALE').
  • Symbols: slug removes unrecognized symbols ('$100' becomes '100', '<5' becomes '5', etc.).
    slugify maps them to words ('$100' becomes 'dollar100', '<5' becomes 'less5', etc.).
  • Empty Output: slug will return a short, predictable hash (' ' becomes 'icag' and '🎉' becomes '8joiq').
    slugify will return an empty string (' ' and '🎉' become '').

Playground

A web playground is available at https://trott.github.io/slug/.

There is also a CLI tool available via npx slug. It doesn't allow you to specify options, so it's utility is minimal.