@arraypress/slug
v1.1.0
Published
URL slug generation with transliteration, collision handling, and flexible options.
Maintainers
Readme
@arraypress/slug
URL slug generation with transliteration, collision handling, and flexible options. Zero dependencies.
Works in Node.js, Cloudflare Workers, Deno, Bun, and browsers.
Install
npm install @arraypress/slugFunctions
slugify(input, options?)
Generate a URL-safe slug from any string.
import { slugify } from '@arraypress/slug';
slugify('Hello World!') // 'hello-world'
slugify('Crème Brûlée Recipe') // 'creme-brulee-recipe'
slugify('Rock & Roll') // 'rock-and-roll'
slugify('Straße nach Berlin') // 'strasse-nach-berlin'
// Options
slugify('Hello World', { separator: '_' }) // 'hello_world'
slugify('Hello World', { lowercase: false }) // 'Hello-World'
slugify('Long Title Here', { maxLength: 10 }) // 'long-title'
slugify('The Art of War', { stopWords: ['the', 'of'] }) // 'art-war'Options: separator (default '-'), lowercase (default true), transliterate (default true), maxLength (default 0 = unlimited), stopWords (default []).
uniqueSlug(input, exists, options?)
Generate a unique slug by appending a counter if needed. Accepts sync or async exists functions.
import { uniqueSlug } from '@arraypress/slug';
// With a database check
const slug = await uniqueSlug('My Product', async (s) => {
const row = await db.prepare('SELECT 1 FROM products WHERE slug = ?').bind(s).first();
return !!row;
});
// => 'my-product' or 'my-product-2' if taken
// With a Set
const taken = new Set(['hello-world', 'hello-world-2']);
const slug = await uniqueSlug('Hello World', (s) => taken.has(s));
// => 'hello-world-3'isValidSlug(slug, options?)
Check if a string is a valid slug.
import { isValidSlug } from '@arraypress/slug';
isValidSlug('hello-world') // true
isValidSlug('Hello World') // false
isValidSlug('hello--world') // false
isValidSlug('hello_world', { separator: '_' }) // trueSTOP_WORDS
Common English stop words for slug generation.
import { slugify, STOP_WORDS } from '@arraypress/slug';
slugify('The Art of War', { stopWords: STOP_WORDS }) // 'art-war'formatLabel(slug, options?)
Convert a slug back into a Title Case display label — the inverse of slugify for display purposes.
import { formatLabel } from '@arraypress/slug';
formatLabel('future-bass') // 'Future Bass'
formatLabel('best-of-the-year') // 'Best Of The Year'
formatLabel('138bpm-trance') // '138bpm Trance' (numeric-leading token kept lowercase)
formatLabel('sound_design', { separator: '_' }) // 'Sound Design'
formatLabel('ai-tools', { preserveCase: ['AI'] }) // 'AI Tools'
formatLabel('daw-midi-export', { preserveCase: ['DAW', 'MIDI'] }) // 'DAW MIDI Export'Use for taxonomy archive headings (/tags/future-bass → "Future Bass"), breadcrumbs, sitemap entries — anywhere you have a slug and don't have the original title cached. Unlike slugify, it preserves stop words because they're usually part of the proper display name.
preserveCase matches case-insensitively against tokens, so passing ['AI', 'DAW'] matches lowercase ai/daw tokens in the slug.
Transliteration
Built-in support for Latin accented, German, Nordic, Turkish, Polish, Czech, and common symbols. & → and, @ → at, € → eur, £ → gbp, © → c, etc.
License
MIT
