Elofyn Tools · About

About the slug generator

← Back to the tool

The wince

Paste Mary%20had%20a%20little%20lamb%21 into a browser bar and the URL works — but nobody actually wants that string to exist. The cleaner version, mary-had-a-little-lamb, is what gets copy-pasted into Slack, printed on receipts, embedded in QR codes, and indexed by search engines. URLs are public, durable, and travel through a lot of pipes that were not designed for percent-encoded Unicode. A slug is the human-friendly, URL-safe segment of a path you choose so the URL survives all of that gracefully.

The job is small but oddly fiddly. You have a string. You want the safe, lower-case, hyphen-separated form of it, with diacritics folded down, non-Latin scripts romanised, repeated separators collapsed, and the whole thing trimmed. You want it instantly. You do not want anyone's server to see your draft post title before you publish it. That last bit is why this tool exists as a client-side page rather than an API endpoint.

What this tool does

The tool wraps @sindresorhus/slugify, an MIT-licensed JavaScript library (~12 kB gzipped including transliteration tables) that combines Unicode-aware transliteration with whitespace, case, and separator normalisation. It runs entirely in your browser via the standard JS string APIs — no server hop, no telemetry, no fetch on the happy path after the page loads. Type or paste into the input and the slug regenerates on every keystroke, deferred so high-priority input render stays smooth.

The options surface the levers that actually matter day-to-day: separator (hyphen, underscore, dot, none), lowercase, decamelize (split getUserById into get-user-by-id), maximum length with trailing-separator strip, and a batch mode that slugs one input line at a time so a column of titles can be processed in one paste.

A brief history

The word “slug” predates URLs. In 20th-century newspaper composing rooms a slug was the short identifier on a line of type — the working name of a headline as it moved through editing, typesetting, and the press. When CMS software borrowed the metaphor in the early 2000s it kept the same intuition: the slug is the short, stable handle for a piece of content, separate from the title that human eyes read. WordPress shipped a post_namefield in 2003 that authors and developers immediately called “the slug.” Movable Type and Drupal carried the same convention. The vocabulary stuck.

The clean-URLs movement pushed slugs from convenience to default. Apache 1.3's mod_rewrite, Rails routing in 2005, and the wave of permalink-first blog platforms made /posts/why-clean-urls-matter the expected shape of a public path, displacing ?id=4231 almost entirely. As the web internationalised, the question became how to slug 京都 or Привет. Two camps emerged: preserve Unicode (the IRI route, formalised in RFC 3987), or transliterate to ASCII. Most CMSes default to transliteration because ASCII slugs survive more downstream tooling intact — log aggregators, regex-based router caches, old filesystems, and email clients that still smuggle URLs through Latin-1 pipes.

Technical principles

The URL-safe alphabet. RFC 3986[1] §2.3 defines the unreserved characters as A–Z, a–z, 0–9, plus the four punctuation marks -, ., _, and ~. Everything else either has reserved structural meaning in URI syntax (/ ? # & = :) or must be percent-encoded for transport. A slug confines itself to a subset of the unreserved set — typically letters and digits plus one separator — which gives it the property that it can sit unencoded in any URL position without negotiation.

Why lowercase by default. URLs are case-sensitive per RFC 3986. In practice many HTTP servers, proxy caches, and analytics tools fold case inconsistently: Apache on a case-insensitive filesystem treats /Posts and /postsas the same resource; an analytics platform reporting hourly might collapse them or split them depending on the rollup version. Lowercasing slugs at mint time removes the entire category of “two pages, same slug, different SEO weight” bug before it can start.

Why hyphens, not underscores. Search engines have long treated hyphens in URL paths as word breaks and underscores as part of a single token. For a slug intended for human consumption — and for any slug a search engine will tokenise — the hyphen is the safer default. The dot earns its place for filename-style segments; the underscore survives because some downstream tools (rare, but real) reject hyphens in identifiers; the empty separator is useful when you want a compact identifier like a CSS class or a variable name.

Transliteration. Stripping diacritics is the easy case: é decomposes into e plus a combining mark via Unicode NFD, the mark gets dropped, and you are done. Romanising non-Latin scripts is the hard case and is locale-dependent. Cyrillic transliteration follows ISO 9 or GOST standards; Japanese uses Hepburn for kana plus a romanisation fallback for Han characters; Chinese uses Hanyu Pinyin; Arabic, Hebrew, Greek, Hangul each have their own conventions. @sindresorhus/slugify delegates to a sibling package that ships a per-script lookup table and applies it in a single pass, which is the right architecture for the size of the problem.

Stability matters more than aesthetics. Tim Berners-Lee's “Cool URIs don't change”[2] makes the case that once a URL is published, its identity is a contract with everyone who linked to it. A good slug is therefore boring: short, stable, free of mechanical artefacts like timestamps or technology suffixes, and chosen at the moment of publication rather than re-derived from the title on every edit. If the title changes, the slug should stay; mint a redirect if you need to indicate the rename.

How to choose options

Separator. Default - for blog posts, public-facing URLs, and anywhere a search engine or a human will read the slug. Use _ for filenames if a downstream tool dislikes hyphens (rare in 2026 but still alive in some legacy data pipelines). Use . for file-extension-style segments. Use none when you want a compact identifier — CSS class, JavaScript variable, GraphQL field — where whitespace would normally be elided rather than hyphenated.

Lowercase. Keep on for URLs. Turn off when the slug is destined for a case-sensitive context where input casing carries meaning — a CamelCase identifier in code, for example, or a Kebab-Case header name that downstream tooling reads back literally.

Decamelize. Keep on to convert getUserById get-user-by-id— the typical “split a code identifier into a readable slug” workflow. Turn off if you want getuserbyid as one token, which is occasionally the right call for filename-style slugs.

Max length. Set when the destination has a hard cap: filesystems (usually 255 bytes), URL routers (commonly 100–200 chars), Twitter handles (15), Slack channels (80), some legacy CMSes that store slugs in a fixed VARCHAR column. The tool strips trailing separators after the cut, so Hello world with maxLength = 6 becomes hello, not hello-.

When not to use a slug. If you need to preserve the original Unicode — an IRI in an internationalised app where the URL itself is the content, such as a Japanese blog where the slug is the post title — do not slugify. Percent-encode at the HTTP layer instead. Slugs are for downsampling to ASCII; IRIs are for keeping the input intact.

Common use cases

  • CMS post URLs (/blog/why-static-sites-win).
  • S3 and object-store keys, where spaces and special characters are technically allowed but make tooling miserable.
  • Database identifiers when a human-readable form is wanted alongside the numeric ID, such as users/jane-doe-2026.
  • Filename normalisation — converting a download like Q3 forecast (final).xlsx into q3-forecast-final.xlsx so a shell loop can iterate over it without quoting every argument.
  • CSS class names, GraphQL field names, JavaScript variable names generated from user-supplied labels — anywhere the identifier grammar is restrictive and the human label is not.

Anti-patterns

  • Keyword-stuffing the slug for SEO. Search-engine guidance has discouraged this for a decade. The slug is a stability handle, not a meta description; packing it with terms invites diminishing returns and produces URLs that age poorly.
  • Embedding the database ID and the slug in the same segment (/posts/4231-why-slugs-matter). Fine for routing, but defeats the “stable URI” goal the moment the numeric ID changes — and IDs do change, on migrations, on merges, on the day someone discovers a duplicate.
  • Auto-regenerating the slug from the title on every edit. Silent breakage of every inbound link. If the title changes, the slug should stay; if you must move the slug, mint a 301.
  • Trusting client-side slug uniqueness. This tool generates the form of a slug, not its uniqueness against an existing dataset. Uniqueness is a server-side constraint and a database concern; check it where the data lives.

References

  1. Berners-Lee, T., Fielding, R., & Masinter, L. (2005). RFC 3986 — Uniform Resource Identifier (URI): Generic Syntax. Internet Engineering Task Force, STD 66. https://www.rfc-editor.org/rfc/rfc3986 — §2.3 defines the unreserved character set this tool's output is confined to; §2.1 defines the percent-encoding alternative the tool helps users avoid.
  2. Berners-Lee, T. (1998). Cool URIs don't change. W3C Style. https://www.w3.org/Provider/Style/URI — the canonical W3C piece on URL stability and the argument that a published URL is a contract with everyone who linked to it.