Skip to content
Devix Open Source

Reference

Options

slug(text, {
  script: 'latin',     // or 'keep'
  lang: 'ar',          // 'ar' | 'fa' | 'ur'
  separator: '-',
  lower: true,
  maxLength: undefined,
  article: true,       // a leading ال becomes al- when romanising
  stopWords: [],
  keep: '',            // extra characters to allow, such as '_'
});
Option Default
script 'latin' 'keep' leaves the Arabic letters in place.
lang 'ar' Which reading to use, and which spelling to unify towards.
separator '-'
lower true
maxLength — Cuts on a separator, never mid-word.
article true المقالة → al-mqala.
stopWords [] Dropped — unless they are the whole title, which would leave nothing.
keep '' Characters that would otherwise be stripped.

uniqueSlug(text, taken, options)

import { uniqueSlug } from '@devix-labs/arabic-slugify';

uniqueSlug('مرحبا', ['mrhba']);             // 'mrhba-2'
uniqueSlug('مرحبا', new Set(['mrhba']));    // 'mrhba-2'

Worth more here than in English. Romanised Arabic collides often, because the short vowels that would tell two words apart are not written — كتب (books) and كاتب (writer) both reduce towards the same consonants. taken is anything iterable, so a Set or the result of a pluck('slug') both work.

prefersScript(text)

prefersScript('مرحبا بالعالم');   // true
prefersScript('Hello world');     // false
prefersScript('Devix متجر');      // true

True when the text is more than half Arabic-script letters. Digits and punctuation do not count, so '2025' is false.

The pieces on their own

import { normalise, transliterate, westernDigits, stripInvisible, hasTashkeel } from '@devix-labs/arabic-slugify';

normalise('مَرْحـــبا');        // 'مرحبا' — marks, kashida and joiners gone
normalise(text, { towards: 'fa' });   // unify towards ی and ک instead of ي and ك
stripInvisible('a‏b');     // 'ab' — joiners and direction marks only
westernDigits('٢٠٢٥');          // '2025'
hasTashkeel('مَرحبا');          // true
transliterate('مُحَمَّد');       // 'muhammad'

normalise is idempotent — normalising a normalised string changes nothing — which is what makes a slug stable across a save, a re-import and a migration.

Searching, not just slugs

The same normalisation is what makes Arabic search work. Store a normalised copy of the field and match against a normalised query, and a search for دنيا finds دنیا, مرحبا finds مَرْحَبًا, and a stray kashida stops mattering:

const searchable = normalise(title);   // alongside the real title

Without it, a user typing a Persian yeh will not find a record stored with an Arabic one, which is the commonest "search is broken" report on Arabic sites.

Updated 15 Sep 2026