slug
PoolModern URL slug generation library - Unicode transliteration, customizable options, unique slugs. Zero dependencies, TypeScript-first, tree-shakeable. Modernizes the slugify ecosystem.
@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/slugifybehavior - 💪 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.slugQuick 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 slugifyoptions(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 charactersreplacements(Record<string, string>): Custom character replacementscustom(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: falseto match slugify) - Option name:
replacement→separator - Option name:
lower→lowercase
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
Taxes calculated at checkout based on your location.