On This Page
String Utilities
The String utilities provide a set of functions for working with strings in JavaScript. These functions help in manipulating, transforming, and analyzing string data efficiently.
Functions
kebabToCamel
function kebabToCamel(str = '', { separator = '_' } = {})Converts a kebab-case string to camelCase. Digit-leading segments are preserved with an underscore prefix to enable lossless round-trips with camelToKebab.
Parameters
| Name | Type | Description |
|---|---|---|
| str | string | The kebab-case string to convert |
| options | object | Optional configuration |
Options
| Name | Type | Default | Description |
|---|---|---|---|
| separator | string | _ |
Character used to encode digit-leading segments |
Returns
The camelCase version of the input string.
Example
import { kebabToCamel } from '@semantic-ui/utils';
console.log(kebabToCamel('background-color')); // 'backgroundColor'console.log(kebabToCamel('grid-2x2')); // 'grid_2x2'console.log(kebabToCamel('heading-1')); // 'heading_1'console.log(kebabToCamel('arrow-down-a-z')); // 'arrowDownAZ'camelToKebab
function camelToKebab(str = '', { lossless = false, separator = '_' } = {})Converts a camelCase string to kebab-case. Underscore-digit sequences are decoded back to hyphen-digit to enable lossless round-trips with kebabToCamel.
By default, a leading uppercase character is silently lowercased to produce a valid HTML attribute name. If lossless is true, leading uppercase is preserved — the output may start with a hyphen (e.g. FooBar → -foo-bar), which is not a valid attribute name but allows an exact round-trip back through kebabToCamel.
Parameters
| Name | Type | Description |
|---|---|---|
| str | string | The camelCase string to convert |
| options | object | Optional configuration |
Options
| Name | Type | Default | Description |
|---|---|---|---|
| lossless | boolean | false |
Preserve leading uppercase for exact round-trip. Output may not be a valid attribute name. |
| separator | string | _ |
Character used to decode digit-leading segments |
Returns
The kebab-case version of the input string.
Example
import { camelToKebab } from '@semantic-ui/utils';
console.log(camelToKebab('backgroundColor')); // 'background-color'console.log(camelToKebab('grid_2x2')); // 'grid-2x2'console.log(camelToKebab('heading_1')); // 'heading-1'console.log(camelToKebab('arrowDownAZ')); // 'arrow-down-a-z'
// PascalCase is normalized by defaultconsole.log(camelToKebab('FooBar')); // 'foo-bar'
// lossless preserves leading uppercase for exact round-tripconsole.log(camelToKebab('FooBar', { lossless: true })); // '-foo-bar'capitalize
function capitalize(str = '')Capitalizes the first character of a string.
Parameters
| Name | Type | Description |
|---|---|---|
| str | string | The string to capitalize |
Returns
The input string with its first character capitalized.
Example
import { capitalize } from '@semantic-ui/utils';
console.log(capitalize('hello')); // 'Hello'console.log(capitalize('WORLD')); // 'WORLD'capitalizeWords
function capitalizeWords(str = '')Capitalizes the first character of each word in a string.
Parameters
| Name | Type | Description |
|---|---|---|
| str | string | The string to capitalize |
Returns
The input string with the first character of each word capitalized.
Example
import { capitalizeWords } from '@semantic-ui/utils';
console.log(capitalizeWords('hello world')); // 'Hello World'console.log(capitalizeWords('THE QUICK BROWN FOX')); // 'The Quick Brown Fox'toTitleCase
function toTitleCase(str = '')Converts a string to title case, taking into account common English articles, conjunctions, and prepositions.
Parameters
| Name | Type | Description |
|---|---|---|
| str | string | The string to convert to title case |
Returns
The input string converted to title case.
Example
import { toTitleCase } from '@semantic-ui/utils';
console.log(toTitleCase('the quick brown fox')); // 'The Quick Brown Fox'console.log(toTitleCase('a tale of two cities')); // 'A Tale of Two Cities'toTitleCase.config
The stop words that stay lowercase mid-title live in toTitleCase.config.stopWords. Style guides disagree here — AP capitalizes longer prepositions, Chicago lowercases them all — so the list is editable once at app boot, and humanize’s titleCase mode reads the same vocabulary.
import { toTitleCase } from '@semantic-ui/utils';
toTitleCase.config.stopWords.push('versus');console.log(toTitleCase('cats versus dogs')); // 'Cats versus Dogs'joinWords
function joinWords(words, options = {})Joins an array of words into a string with customizable separators and formatting.
Parameters
| Name | Type | Description |
|---|---|---|
| words | array | The array of words to join |
| options | object | Optional configuration object |
Options
| Name | Type | Default | Description |
|---|---|---|---|
| separator | string | ’, ’ | The separator between words |
| lastSeparator | string | ’ and ’ | The separator before the last word |
| oxford | boolean | true | Whether to use an Oxford comma |
| quotes | boolean | false | Whether to wrap words in quotes |
| transform | function | null | A function to transform each word |
Returns
A string of joined words according to the specified options.
Example
import { joinWords } from '@semantic-ui/utils';
const fruits = ['apple', 'banana', 'cherry'];
console.log(joinWords(fruits));// 'apple, banana, and cherry'
console.log(joinWords(fruits, { oxford: false }));// 'apple, banana and cherry'
console.log(joinWords(fruits, { quotes: true }));// '"apple", "banana", and "cherry"'
console.log(joinWords(fruits, { transform: word => word.toUpperCase() }));// 'APPLE, BANANA, and CHERRY'getArticle
function getArticle(word, settings = {})Determines the appropriate indefinite article (‘a’ or ‘an’) for a given word, using a vowel check plus an exceptions vocabulary for words whose sound contradicts their spelling (hour, university).
Parameters
| Name | Type | Description |
|---|---|---|
| word | string | The word to determine the article for |
| settings | object | Optional settings object |
Settings
| Name | Type | Default | Description |
|---|---|---|---|
| capitalize | boolean | false | Whether to capitalize the article |
Returns
The appropriate indefinite article (‘a’ or ‘an’) for the given word.
Example
import { getArticle } from '@semantic-ui/utils';
console.log(getArticle('apple')); // 'an'console.log(getArticle('banana')); // 'a'console.log(getArticle('hour', { capitalize: true })); // 'An' (exceptions vocabulary)console.log(getArticle('university')); // 'a' (sounds like "you")getArticle.config
The exceptions vocabulary lives in getArticle.config.exceptions, a lowercased word mapped to its article, checked before the vowel heuristic. It ships seeded with the common sound-contradicts-spelling words and is editable once at app boot.
import { getArticle } from '@semantic-ui/utils';
getArticle.config.exceptions.faq = 'an';console.log(getArticle('FAQ', { includeWord: true })); // 'an FAQ'truncate
function truncate(text, length, options = {})Truncates text to a specified length with intelligent word boundary handling and Unicode support.
Parameters
| Name | Type | Description |
|---|---|---|
| text | string | The text to truncate |
| length | number | Maximum length of the output |
| options | object | Optional configuration |
Options
| Name | Type | Default | Description |
|---|---|---|---|
| suffix | string | ‘…’ | Text to append when truncated |
| wordBoundary | boolean | true | Whether to truncate at word boundaries |
| locale | string | ‘en’ | Locale for word segmentation |
Returns
The truncated text with suffix if needed, or original text if shorter than length.
Example
import { truncate } from '@semantic-ui/utils';
// Basic usageconsole.log(truncate('This is a long text that needs truncating', 20));// 'This is a long text…'
// Disable word boundaryconsole.log(truncate('This is a long sentence', 15, { wordBoundary: false }));// 'This is a very…'
// Custom suffixconsole.log(truncate('This is a test', 10, { suffix: ' [more]' }));// 'This [more]'
// Handles emojis correctlyconsole.log(truncate('Hello 👋 World 🌍', 10));// 'Hello 👋…'
// Locale-aware segmentationconsole.log(truncate('こんにちは世界です', 8, { locale: 'ja' }));// 'こんにちは…'These string utilities provide a robust set of tools for working with strings in JavaScript, enhancing productivity and code readability. The notes highlight some of the unique aspects and potential limitations of certain functions.
tokenize
function tokenize(str = '')Converts a string into a token by replacing spaces with hyphens and removing non-word characters.
Parameters
| Name | Type | Description |
|---|---|---|
| str | string | The string to tokenize |
Returns
A tokenized version of the input string.
Example
import { tokenize } from '@semantic-ui/utils';
console.log(tokenize("Hello World!")); // "hello-world"console.log(tokenize("User's Name_123")); // "users-name-123"fill
function fill(template, values, { defaults, transform, missing, open = '{', close = '}' } = {})Fills bracketed placeholders in a string from a values object — the shape a copy table takes, where a whole sentence stays one editable string and only its variable parts are named. Keeping the sentence whole is what lets a later translation pass swap it, which joining fragments at runtime does not.
Values resolve from values first and defaults second, so a copy table can carry default values and a call override the ones it knows. A key that resolves to nothing leaves its placeholder in place, so a typo in a copy table shows itself in the UI rather than shipping a blank sentence. Pass missing to choose otherwise.
Keys read the path grammar, so {user.name} reaches into a nested object and an array of values reads positionally ({0}). A key held flat wins over traversal, so { 'user.name': 'jack' } also resolves {user.name}.
Substitution runs once and the result is never rescanned, so a filled value carrying {other} cannot reach a second key.
A value a string cannot faithfully hold — a plain object, a Date, NaN, Infinity, a function — reads as missing rather than as [object Object]. transform is where an app says how those print, usually through formatDate or its own display helper.
This is a fill, not a template engine: {key} is a name, never an expression, and there is no escape for a literal brace. Under the default policy none is needed, since a {...} that names nothing is left exactly as written.
Parameters
| Name | Type | Description |
|---|---|---|
| template | string | The string carrying placeholders. A non-string reads as empty |
| values | object, array | The values to fill from, keyed by placeholder name. An array reads positionally |
| options | object | Optional configuration |
Options
| Name | Type | Default | Description |
|---|---|---|---|
| defaults | object, array | — | Values used when a key is absent or nullish in values |
| transform | function | — | (value, key) applied to each resolved value before it is stringified |
| missing | string, function | — | What stands in for a placeholder whose key resolves to nothing. A string substitutes verbatim, a function receives (key, placeholder). By default the placeholder is left in place |
| open | string | { |
Opening delimiter |
| close | string | } |
Closing delimiter |
Returns
The filled string.
Example
import { fill, formatDate } from '@semantic-ui/utils';
console.log(fill('Name {chosenName} is already taken', { chosenName: 'jack' })); // 'Name jack is already taken'
// an unresolved key keeps its placeholder, so the typo shows itselfconsole.log(fill('Welcome back, {frist}', { first: 'jack' })); // 'Welcome back, {frist}'console.log(fill('Welcome back, {first}', {}, { missing: 'friend' })); // 'Welcome back, friend'
// values first, defaults secondconst copy = { appName: 'Semantic', plan: 'free' };console.log(fill('{appName} on the {plan} plan', { plan: 'pro' }, { defaults: copy })); // 'Semantic on the pro plan'
// keys read the path grammarconsole.log(fill('{user.name} signed in', { user: { name: 'jack' } })); // 'jack signed in'
// transform says how a value printsconsole.log(fill('Renews {date}', { date: new Date('2026-01-02') })); // 'Renews {date}'console.log(fill('Renews {date}', { date: new Date('2026-01-02') }, { transform: (value) => formatDate(value, 'LL'),})); // 'Renews January 2, 2026'
// another placeholder grammarconsole.log(fill('Hello ${name}', { name: 'jack' }, { open: '${', close: '}' })); // 'Hello jack'humanize
function humanize(str = '', options = {})Converts a machine identifier into human-readable label text, the display-side inverse of tokenize. Splits snake_case, kebab-case, camelCase, and PascalCase, keeping acronym runs intact, and sentence-cases the result. Common lowercase acronyms (id, url, api) are rescued by a built-in vocabulary you can extend app-wide through humanize.config.
Parameters
| Name | Type | Description |
|---|---|---|
| str | string | The identifier to humanize |
| options | object | Optional configuration |
Options
| Name | Type | Default | Description |
|---|---|---|---|
| titleCase | boolean | false | Title-case the result instead of sentence case, keeping stop words lowercase |
| dropId | boolean | true | Drop a trailing id segment, e.g. user_id becomes User |
| constantCase | boolean | false | Treat input as CONSTANT_CASE, sentence-casing shouting enums like IN_PROGRESS instead of preserving them as acronyms |
| terms | object | — | Per-call term overrides ({ oauth: 'OAuth' }), a lowercased token mapped to its display string, layered over humanize.config.terms |
Returns
The humanized label, or '' for empty or non-string input.
Example
import { humanize } from '@semantic-ui/utils';
// Basic usageconsole.log(humanize('first_name')); // 'First name'console.log(humanize('user-profile')); // 'User profile'console.log(humanize('createdAt')); // 'Created at'
// Keeps acronyms whole, including pluralsconsole.log(humanize('getHTTPResponse')); // 'Get HTTP response'console.log(humanize('getURLsFromPage')); // 'Get URLs from page'
// Drops a trailing id by defaultconsole.log(humanize('user_id')); // 'User'console.log(humanize('order_id', { dropId: false })); // 'Order ID'
// Built-in vocabulary rescues common lowercase acronymsconsole.log(humanize('api_url')); // 'API URL'console.log(humanize('avatar_url')); // 'Avatar URL'
// Title case for headers, respecting stop wordsconsole.log(humanize('terms_of_service', { titleCase: true })); // 'Terms of Service'
// Sentence-case a shouting enum for a status labelconsole.log(humanize('IN_PROGRESS', { constantCase: true })); // 'In progress'humanize.config
humanize.config holds the global defaults plus a terms vocabulary, a lowercased token mapped to its exact display string. It ships seeded with { id: 'ID', url: 'URL', api: 'API' }. Set it once at app boot and every call inherits it, so a large codebase pins its vocabulary in one place. A per-call terms option layers over it for one-off overrides.
import { humanize } from '@semantic-ui/utils';
// set an app-wide vocabulary oncehumanize.config.terms.sku = 'SKU';humanize.config.terms.oauth = 'OAuth';
console.log(humanize('product_sku')); // 'Product SKU'console.log(humanize('oauth_token')); // 'OAuth token'
// a per-call term layers over the global vocabularyconsole.log(humanize('gpu_count', { terms: { gpu: 'GPU' } })); // 'GPU count'escapeHTML
function escapeHTML(string)Escapes HTML special characters in a string. Returns an empty string for falsy input.
Parameters
| Name | Type | Description |
|---|---|---|
| string | string | The string to escape |
Returns
A new string with HTML special characters escaped, or '' if the input is falsy.
Example
import { escapeHTML } from '@semantic-ui/utils';
const str = '<script>alert("XSS")</script>';console.log(escapeHTML(str)); // "<script>alert("XSS")</script>"
console.log(escapeHTML(null)); // ''console.log(escapeHTML(undefined)); // ''console.log(escapeHTML('')); // ''unescapeHTML
function unescapeHTML(string)Converts HTML entities back to their original characters. The inverse of escapeHTML.
Parameters
| Name | Type | Description |
|---|---|---|
| string | string | The string containing HTML entities to unescape |
Returns
A new string with HTML entities converted back to characters.
Example
import { unescapeHTML } from '@semantic-ui/utils';
console.log(unescapeHTML('<div>Hello</div>')); // '<div>Hello</div>'console.log(unescapeHTML('rock & roll')); // 'rock & roll'reverseString
function reverseString(str = '', options = {})Reverses a string while properly handling Unicode grapheme clusters including emojis, flags, skin tones, and combined characters.
Parameters
| Name | Type | Description |
|---|---|---|
| str | string | The string to reverse |
| options | object | Optional configuration |
Options
| Name | Type | Default | Description |
|---|---|---|---|
| locale | string | ‘en’ | Locale for grapheme segmentation |
Returns
The reversed string with grapheme clusters preserved.
Example
import { reverseString } from '@semantic-ui/utils';
// Basic usageconsole.log(reverseString('hello')); // 'olleh'console.log(reverseString('hello world')); // 'dlrow olleh'
// Handles emojis correctlyconsole.log(reverseString('Hello 👋')); // '👋 olleH'console.log(reverseString('🎉🎊🎈')); // '🎈🎊🎉'
// Preserves complex grapheme clustersconsole.log(reverseString('🇺🇸🇬🇧')); // '🇬🇧🇺🇸' (flag emojis)console.log(reverseString('Hello 👋🏽')); // '👋🏽 olleH' (emoji with skin tone)editDistance
function editDistance(a, b, options = {})Counts the single-character edits (inserts, deletes, replacements, and optionally adjacent swaps) that turn one string into the other. 0 means identical. Comparison is Unicode-aware: strings are NFC-normalized first and astral characters count as one edit, with full grapheme cluster comparison behind the grapheme option.
For filtering many candidates, max caps the search: the comparison stops as soon as the distance provably exceeds it and returns Infinity, so editDistance(a, b, { max: 2 }) <= 2 stays cheap on far-apart strings.
Parameters
| Name | Type | Description |
|---|---|---|
| a | string | The first string (nullish reads as empty, other input is stringified) |
| b | string | The second string |
| options | object | Optional configuration |
Options
| Name | Type | Default | Description |
|---|---|---|---|
| max | number | Infinity | Stop once the distance provably exceeds this and return Infinity |
| ignoreCase | boolean | false | Lowercase both strings before comparing |
| normalize | boolean | true | Apply Unicode NFC normalization first, so canonically equivalent spellings read as identical |
| grapheme | boolean | false | Compare grapheme clusters via Intl.Segmenter, so a flag or family emoji is one unit |
| locale | string | ‘en’ | Locale for grapheme segmentation |
| swaps | boolean | false | Count an adjacent swap as one edit (the restricted Damerau-Levenshtein reading) |
| insertCost | number | 1 | Cost of inserting a character |
| deleteCost | number | 1 | Cost of deleting a character |
| replaceCost | number | 1 | Cost of replacing a character |
| swapCost | number | 1 | Cost of an adjacent swap when swaps is on |
Returns
The edit distance as a number, or Infinity when it exceeds max. With custom costs the measure is directional: it transforms a into b, so deletions remove characters of a and insertions add characters of b.
Example
import { editDistance } from '@semantic-ui/utils';
console.log(editDistance('kitten', 'sitting')); // 3console.log(editDistance('Color', 'colour', { ignoreCase: true })); // 1
// the classic typo reads as one edit with swapsconsole.log(editDistance('teh', 'the')); // 2console.log(editDistance('teh', 'the', { swaps: true })); // 1
// cap the search for cheap candidate filteringconsole.log(editDistance('banana', 'orange', { max: 2 })); // Infinity
// unicode-aware by defaultconsole.log(editDistance('café', 'cafe\u0301')); // 0 (NFC-normalized)console.log(editDistance('👍', '👎')); // 1 (code points, not surrogate halves)console.log(editDistance('👩🏽🚀', '🧑🏻🚀', { grapheme: true })); // 1similarity
function similarity(a, b, options = {})Scores how alike two strings read, from 0 (nothing shared) to 1 (identical): one minus the edit distance over the longer string’s length. Unit edit costs by definition, with the same text options as editDistance.
min sets a score floor: a comparison that cannot reach it stops early and reports 0, which keeps fuzzy-match loops cheap.
Parameters
| Name | Type | Description |
|---|---|---|
| a | string | The first string (nullish reads as empty, other input is stringified) |
| b | string | The second string |
| options | object | Optional configuration |
Options
| Name | Type | Default | Description |
|---|---|---|---|
| min | number | 0 | Score floor for early exit — below it the comparison stops and reports 0 |
| ignoreCase | boolean | false | Lowercase both strings before comparing |
| normalize | boolean | true | Apply Unicode NFC normalization first |
| grapheme | boolean | false | Compare grapheme clusters via Intl.Segmenter |
| locale | string | ‘en’ | Locale for grapheme segmentation |
| swaps | boolean | false | Count an adjacent swap as one edit |
Returns
A score from 0 to 1. Two empty strings score 1.
Example
import { similarity } from '@semantic-ui/utils';
console.log(similarity('kitten', 'sitting')); // 0.571…console.log(similarity('hello', 'hello')); // 1console.log(similarity('abc', 'xyz')); // 0
console.log(similarity('JavaScript', 'javascript', { ignoreCase: true })); // 1console.log(similarity('recieve', 'receive', { swaps: true })); // 0.857…
// early-exit floor for fuzzy filteringconsole.log(similarity('kitten', 'sitting', { min: 0.9 })); // 0suggest
function suggest(word, candidates, options = {})Suggests the nearest candidate to a mistyped word, or null when nothing reads close enough to say confidently — the lookup behind a did-you-mean teaching message. A suggestion is optional by nature, so returning null on a weak match is the point. It returns the candidate verbatim, never prose, so the caller owns the sentence around it.
Candidates read generously: an array or Set of names, a lone name, or the keys of a plain object or Map — the vocabularies callers already hold. Case differences and adjacent swaps read as typos by default.
Pass count for the plausible candidates as an array, best-first, up to count of them ([] when none) — the same confidence gate, for messages that offer choices. To rank everything under an explicit edit cap instead, filter with editDistance and max: suggest is the judgment call, editDistance is the ruler.
Parameters
| Name | Type | Description |
|---|---|---|
| word | string | The mistyped word |
| candidates | array, string, object, Map, Set | The known vocabulary. Objects and Maps read as their keys, nullish entries are skipped |
| options | object | Optional configuration |
Options
| Name | Type | Default | Description |
|---|---|---|---|
| threshold | number | 0.6 | Minimum similarity (0..1) a candidate must reach to be suggested, so the bar scales with word length |
| ignoreCase | boolean | true | Read case differences as typos |
| swaps | boolean | true | Read adjacent swaps as single typos |
| count | number | — | Return the plausible candidates as an array, best-first, up to this many |
Returns
The nearest candidate as a string, or null when none reaches the threshold. With count, an array of candidates best-first, [] when none. Ties keep the earlier candidate, so order candidates by preference when it matters.
Example
import { suggest } from '@semantic-ui/utils';
console.log(suggest('stirng', ['string', 'number', 'boolean'])); // 'string'console.log(suggest('xyz', ['string', 'number'])); // null
// a config's options or a registry's keys are already a vocabularyconsole.log(suggest('colr', { color: '#f00', size: 'large' })); // 'color'
// case and swap typos resolve to the canonical spellingconsole.log(suggest('FORCE', ['force', 'quiet'])); // 'force'console.log(suggest('sroted', ['sorted', 'sordid'])); // 'sorted'
// count offers choices, best-firstconsole.log(suggest('stirng', ['strong', 'string'], { count: 3 })); // ['string', 'strong']
// the caller owns the sentenceconst match = suggest('paddign', ['padding', 'margin']);const hint = match ? `, did you mean '${match}'?` : '';These string utilities provide helpful tools for working with strings and text manipulation in JavaScript, enhancing string processing capabilities and security in web applications.