A comprehensive TypeScript library for converting strings into URL-friendly slugs with support for unicode, emojis, multiple languages, custom separators, max length limits, validation, and utility functions.
- ✅ Unicode Support: Handles accented characters, Cyrillic, Greek, and other unicode characters
- ✅ Emoji Handling: Removes or preserves emojis based on configuration
- ✅ Multi-language: Supports diacritics and special characters from multiple languages
- ✅ Locale Support: 13+ locale-specific character and word replacements (bg, sr-c, sr-l, tr, ka, kk, vi, de, es, fr, pt, nb, sv)
- ✅ Custom Separators: Configurable separator character (default:
-) - ✅ Max Length: Truncate slugs to maximum length while preserving word boundaries
- ✅ Strict Mode: Option to remove all non-alphanumeric characters
- ✅ Case Preservation: Option to preserve original case
- ✅ Custom Replacements: Define custom character replacement maps
- ✅ Utility Functions: Validate slugs, deslugify, and generate unique slugs
- ✅ TypeScript: Full TypeScript support with type definitions
- ✅ Zero Dependencies: No external dependencies required
# Using Bun
bun add slugify-ts
# Using npm
npm install slugify-ts
# Using pnpm
pnpm add slugify-ts
# Using yarn
yarn add slugify-tsimport slugify from 'slugify-ts';
// Basic usage
slugify('Hello World!'); // 'hello-world'
// With options
slugify('Café & Restaurant', { separator: '_' }); // 'cafe_restaurant'
slugify('Very Long Title', { maxLength: 10 }); // 'very-long'Converts a string into a URL-friendly slug.
Parameters:
input(string | null | undefined): The string to convert to a slugoptions(SlugifyOptions, optional): Configuration options
Returns: string - A URL-friendly slug
Options:
separator(string, default:'-'): Character to use as separator between wordsmaxLength(number, optional): Maximum length of the resulting slugstrict(boolean, default:false): Remove all non-alphanumeric characters (except separator)preserveCase(boolean, default:false): Keep the original case of the inputreplacementMap(Record<string, string>, optional): Custom character replacement mapremoveEmojis(boolean, default:true): Remove emojis from the inputlocale(string, optional): Locale code for locale-specific character and word replacements. Supported locales:'bg','sr-c','sr-l','tr','ka','kk','vi','de','es','fr','pt','nb','sv'
Validates if a string is a valid slug.
Parameters:
slug(string): The string to validateseparator(string, default:'-'): The separator character used in the slug
Returns: boolean - True if the string is a valid slug
Converts a slug back to readable text (approximate).
Parameters:
slug(string): The slug to convert back to readable textseparator(string, default:'-'): The separator character used in the slug
Returns: string - A readable text approximation of the slug
Generates a unique slug by appending a counter if the slug already exists.
Parameters:
slug(string): The base slug to make uniqueexistingSlugs(string[]): Array of existing slugs to check againstseparator(string, default:'-'): The separator character to use before the counter
Returns: string - A unique slug with a counter appended if necessary
Convenience function that combines slugify and slugifyWithCounter.
Parameters:
input(string | null | undefined): The string to convert to a slugexistingSlugs(string[], default:[]): Array of existing slugs to check againstoptions(SlugifyOptions, optional): Configuration options for slugification
Returns: string - A unique slug
import slugify from 'slugify-ts';
slugify('Hello World!'); // 'hello-world'
slugify('My Awesome Blog Post'); // 'my-awesome-blog-post'import slugify from 'slugify-ts';
// Accented characters
slugify('Café'); // 'cafe'
slugify('Résumé'); // 'resume'
slugify('São Paulo'); // 'sao-paulo'
// Cyrillic
slugify('Москва'); // 'moskva'
// Greek
slugify('Αθήνα'); // 'Athina'
// Special symbols
slugify('Copyright © 2024'); // 'copyright-c-2024'
slugify('Price: $100'); // 'price-dollar100'import slugify from 'slugify-ts';
slugify('Hello World', { separator: '_' }); // 'hello_world'
slugify('Test String', { separator: '.' }); // 'test.string'import slugify from 'slugify-ts';
slugify('Very Long Title Here', { maxLength: 10 }); // 'very-long'
slugify('Hello World Test', { maxLength: 12 }); // 'hello-world'import slugify from 'slugify-ts';
slugify('Hello-World!', { strict: true }); // 'hello-world'
slugify('Test@123', { strict: true }); // 'test123'import slugify from 'slugify-ts';
slugify('Hello World', { preserveCase: true }); // 'Hello-World'
slugify('MY TITLE', { preserveCase: true }); // 'MY-TITLE'import slugify from 'slugify-ts';
slugify('Hello & World', {
replacementMap: { '&': 'and' },
}); // 'hello-and-world'import slugify from 'slugify-ts';
// Emojis are removed by default
slugify('Hello 👋 World'); // 'hello-world'
// To preserve emojis (they'll still be converted to separators)
slugify('Hello 👋 World', { removeEmojis: false }); // 'hello-world'import slugify from 'slugify-ts';
// German locale - umlauts become 'ae', 'oe', 'ue'
slugify('Ä ö Ü', { locale: 'de' }); // 'ae-oe-ue'
// Spanish locale - symbols get Spanish translations
slugify('% & < >', { locale: 'es' }); // 'por-ciento-y-menor-que-mayor-que'
// Vietnamese locale - 'đ' becomes 'd' instead of 'dj'
slugify('Đ đ', { locale: 'vi' }); // 'd-d'
// Norwegian locale - special characters
slugify('& Å Æ Ø', { locale: 'nb' }); // 'og-aa-ae-oe'
// Supported locales: bg, sr-c, sr-l, tr, ka, kk, vi, de, es, fr, pt, nb, svimport {
isValidSlug,
deslugify,
slugifyWithCounter,
uniqueSlugify,
} from 'slugify-ts';
// Validate slugs
isValidSlug('hello-world'); // true
isValidSlug('hello--world'); // false (consecutive separators)
isValidSlug('-hello-world'); // false (starts with separator)
// Convert slug back to readable text
deslugify('hello-world'); // 'Hello World'
deslugify('my-awesome-blog-post'); // 'My Awesome Blog Post'
// Generate unique slugs
const existing = ['hello-world', 'hello-world-1'];
slugifyWithCounter('hello-world', existing); // 'hello-world-2'
// Convenience function
uniqueSlugify('Hello World!', existing); // 'hello-world-2'import slugify, { type SlugifyOptions } from 'slugify-ts';
const options: SlugifyOptions = {
separator: '_',
maxLength: 20,
strict: false,
};
const slug = slugify('My Title', options);The library handles various edge cases:
- Empty/null/undefined inputs: Returns empty string
- Consecutive separators: Automatically removed
- Leading/trailing separators: Automatically trimmed
- Very long strings: Handled efficiently
- Special characters: Converted or removed based on options
- Numbers: Preserved in slugs
Run tests using Bun:
bun testOr watch mode:
bun test --watchMIT
Contributions are welcome! Please feel free to submit a Pull Request.
- Initial release
- Core slugify function with unicode support
- Utility functions (isValidSlug, deslugify, slugifyWithCounter)
- Comprehensive test suite
- Full TypeScript support