Neo Zero

slug

Pool

Modern URL slug generation library - Unicode transliteration, customizable options, unique slugs. Zero dependencies, TypeScript-first, tree-shakeable. Modernizes the slugify ecosystem.

$ lpm install @lpm.dev/neo.slug

@lpm.dev/neo.slug

Modern, fast, and lightweight URL slug generation for JavaScript

Why neo.slug?

  • 🚀 Blazing Fast: 100-400x faster than @sindresorhus/slugify
  • 📦 Tiny Bundle: ~2.3 KB gzipped (vs 7 KB for slugify)
  • 🌲 Tree-Shakeable: Import only what you need
  • 🎯 Modern Behavior: Matches @sindresorhus/slugify behavior
  • 💪 TypeScript First: Built with TypeScript strict mode
  • 🌍 Unicode Support: Handles German, Nordic, Polish, Czech, Turkish, and more
  • ⚙️ Flexible: Extensive configuration options
  • 0️⃣ Zero Dependencies: No external runtime dependencies

Performance

Benchmark results (operations per second, higher is better):

Library Simple Strings Unicode camelCase Complex Content
neo.slug 228,579 ops/s 213,166 ops/s 205,426 ops/s 171,539 ops/s
slugify 1,214,806 ops/s 1,053,443 ops/s 1,080,891 ops/s 366,818 ops/s
@sindresorhus/slugify 1,642 ops/s 2,281 ops/s 2,217 ops/s 1,777 ops/s

neo.slug is 100-400x faster than @sindresorhus/slugify while matching its modern behavior!

Installation

lpm install @lpm.dev/neo.slug

Quick Start

import { slug } from "@lpm.dev/neo.slug";

// Basic usage
slug("Hello World"); // 'hello-world'
slug("Café München"); // 'cafe-munchen'
slug("I ♥ JavaScript"); // 'i-love-javascript'

// camelCase conversion
slug("fooBarBaz"); // 'foo-bar-baz'
slug("ES6, ES7, ES8"); // 'es-6-es-7-es-8'

// Custom options
slug("Hello World", { separator: "_" }); // 'hello_world'
slug("Hello World", { lowercase: false }); // 'Hello-World'
slug("[email protected]", { strict: true }); // 'helloworld-com'

API

slug(input, options?)

Convert a string to a URL-safe slug.

Parameters:

  • input (string): String to slugify
  • options (object, optional): Configuration options

Options:

  • separator (string): Character to separate words (default: '-')
  • lowercase (boolean): Convert to lowercase (default: true)
  • strict (boolean): Remove all non-alphanumeric except separator (default: false)
  • trim (boolean): Trim leading/trailing separators (default: true)
  • locale (string): Locale for case conversion (default: system locale)
  • preserve (string[]): Characters to preserve (not treat as separators)
  • remove (RegExp): Regex pattern to remove characters
  • replacements (Record<string, string>): Custom character replacements
  • custom (function): Custom transliteration function

Returns: URL-safe slug string

Examples:

// Separator
slug("Hello World", { separator: "_" }); // 'hello_world'
slug("foo bar baz", { separator: "." }); // 'foo.bar.baz'

// Lowercase
slug("Hello World", { lowercase: false }); // 'Hello-World'

// Strict mode (remove all non-alphanumeric except separator)
slug("[email protected]", { strict: true }); // 'helloworld-com'
slug("foo#bar$baz", { strict: true }); // 'foobarbaz'

// Trim
slug("-hello world-", { trim: false }); // '-hello-world-'

// Preserve characters
slug("hello.world", { preserve: ["."] }); // 'hello.world'
slug("user_name_123", { preserve: ["_"] }); // 'user_name_123'

// Remove specific characters
slug("Hello*World", { remove: /[*]/g }); // 'hello-world'

// Custom replacements
slug("foo & bar", { replacements: { "&": "n" } }); // 'foo-n-bar'

// Custom transliteration
slug("foo123", {
  custom: (str) => str.replace(/\d/g, (n) => ["zero", "one", "two"][n]),
}); // 'fooonetwothree'

Advanced Features

createSlugger(defaultOptions)

Create a reusable slugger function with preset options.

import { createSlugger } from "@lpm.dev/neo.slug";

const blogSlugger = createSlugger({
  separator: "-",
  lowercase: true,
  strict: true,
});

blogSlugger("Hello World"); // 'hello-world'
blogSlugger("Café München"); // 'cafe-munchen'

// Override options per call
blogSlugger("Hello World", { lowercase: false }); // 'Hello-World'

uniqueSlug(input, checker, options?)

Generate unique slugs with automatic collision handling.

import { uniqueSlug } from "@lpm.dev/neo.slug";

// With a Set
const existing = new Set(["hello-world"]);
await uniqueSlug("Hello World", existing); // 'hello-world-2'

// With a function (e.g., database check)
const checkDatabase = async (slug: string) => {
  return await db.posts.findOne({ slug });
};
await uniqueSlug("My Post", checkDatabase); // 'my-post' or 'my-post-2', etc.

// Synchronous version
import { uniqueSlugSync } from "@lpm.dev/neo.slug";
const existing = new Set(["test"]);
uniqueSlugSync("Test", existing); // 'test-2'

Unicode Support

neo.slug handles Unicode characters using a hybrid approach:

  • NFD normalization for diacritics (é → e, ñ → n, ü → u, ö → o, ä → a)
  • Custom maps for characters NFD doesn't handle:

    • Germanic: ß → ss
    • Nordic: å → aa, ø → o, æ → ae
    • Polish: ł → l, ą → a, ć → c, ę → e, ń → n, ó → o, ś → s, ź → z, ż → z
    • Czech: č → c, ď → d, ě → e, ň → n, ř → r, š → s, ť → t, ů → u, ž → z
    • Turkish: ı → i, ğ → g, ş → s
    • Romanian: ă → a, â → a, î → i, ș → s, ț → t
    • Vietnamese: đ → d

Examples:

slug("München"); // 'munchen'
slug("Zürich"); // 'zurich'
slug("Malmö"); // 'malmo'
slug("Łódź"); // 'lodz'
slug("Kraków"); // 'krakow'
slug("Café"); // 'cafe'

Real-World Use Cases

Blog Post URLs

const blogSlugger = createSlugger({ strict: true });

blogSlugger("10 Ways to Improve Your JavaScript Skills");
// '10-ways-to-improve-your-javascript-skills'

blogSlugger("React vs Vue vs Angular: Which One to Choose?");
// 'react-vs-vue-vs-angular-which-one-to-choose'

Product URLs

slug('MacBook Pro 14" (2024)'); // 'macbook-pro-14-2024'
slug("iPhone 15 Pro Max - 256GB"); // 'iphone-15-pro-max-256-gb'
slug("Sony WH-1000XM5 Wireless Headphones"); // 'sony-wh-1000-xm-5-wireless-headphones'

User Profiles

const usernameSlugger = createSlugger({
  separator: "-",
  lowercase: true,
  strict: true,
});

// With unique slug generation
const username = await uniqueSlug("John Smith", (slug) =>
  db.users.exists({ username: slug }),
);
// 'john-smith' or 'john-smith-2', 'john-smith-3', etc.

Tree-Shaking

Import only what you need for optimal bundle size:

// Import just the core function (~2 KB)
import { slug } from "@lpm.dev/neo.slug/slug";

// Import core + advanced features (~2.5 KB)
import { slug, createSlugger, uniqueSlug } from "@lpm.dev/neo.slug";

// Or use subpath imports for maximum tree-shaking
import { slug } from "@lpm.dev/neo.slug/slug";
import { createSlugger } from "@lpm.dev/neo.slug/advanced";

Migration Guides

From slugify

neo.slug is mostly compatible with slugify:

// Before (slugify)
import slugify from "slugify";
slugify("Hello World"); // 'Hello-World'
slugify("Hello World", { lower: true }); // 'hello-world'
slugify("Hello World", { replacement: "_" }); // 'Hello_World'
slugify("Hello World", { strict: true }); // 'Hello-World'

// After (neo.slug)
import { slug } from "@lpm.dev/neo.slug";
slug("Hello World"); // 'hello-world' (lowercase by default!)
slug("Hello World", { lowercase: false }); // 'Hello-World'
slug("Hello World", { separator: "_" }); // 'hello_world'
slug("Hello World", { strict: true }); // 'hello-world'

Key differences:

  • neo.slug lowercases by default (set lowercase: false to match slugify)
  • Option name: replacementseparator
  • Option name: lowerlowercase

From @sindresorhus/slugify

neo.slug matches @sindresorhus/slugify behavior:

// Before (@sindresorhus/slugify)
import slugify from "@sindresorhus/slugify";
slugify("Hello World"); // 'hello-world'
slugify("I ♥ Dogs"); // 'i-love-dogs'
slugify("Café"); // 'cafe'

// After (neo.slug)
import { slug } from "@lpm.dev/neo.slug";
slug("Hello World"); // 'hello-world' ✓
slug("I ♥ Dogs"); // 'i-love-dogs' ✓
slug("Café"); // 'cafe' ✓

Differences:

  • neo.slug is 100-400x faster!
  • Same behavior for all common cases

Bundle Size Comparison

Package Min + Gzip Tree-shakeable
@lpm.dev/neo.slug ~2.3 KB ✅ Yes
slugify ~3.5 KB ❌ No
@sindresorhus/slugify ~7 KB ❌ No

TypeScript

Full TypeScript support with strict mode enabled:

import { slug, SlugOptions } from "@lpm.dev/neo.slug";

const options: SlugOptions = {
  separator: "-",
  lowercase: true,
  strict: false,
  trim: true,
};

const result: string = slug("Hello World", options);

Browser Support

Works in all modern browsers and Node.js 18+:

  • Chrome, Firefox, Safari, Edge (latest 2 versions)
  • Node.js 18+
  • Deno, Bun

License

MIT

lpmslugslugifyurlurl-slugpermalinkseofriendly-urlclean-urltransliteratetransliterationunicodei18ndiacriticsunique-slugtypescriptzero-dependencytree-shakeableesmmodern
Unlimited AccessInstall as many Pool packages as you need.
Fund Real WorkEvery install you run sends revenue directly to the developer who built it.

Taxes calculated at checkout based on your location.

Weekly Installs
5
Version
1.0.0
Published
LicenseMIT
Size233.71 KB
Files27
Node version>= 18
TypeScriptYes