A lightweight, zero-dependency utility library for web development. Provides type-safe helpers for arrays, strings, numbers, dates, files, and common regex patterns - fully tree-shakeable with ESM and CommonJS support.
npm install @stanvanheumen/web-utilsimport { groupByArray, isEmail, blobToFile } from '@stanvanheumen/web-utils';CommonJS is also supported:
const { groupByArray, isEmail, blobToFile } = require('@stanvanheumen/web-utils');| Function | Browser | Node.js |
|---|---|---|
clamp |
✓ | ✓ |
round |
✓ | ✓ |
chunkArray |
✓ | ✓ |
groupByArray |
✓ | ✓ |
mapArray |
✓ | ✓ |
removeFromArray |
✓ | ✓ |
sortArray |
✓ | ✓ |
toggleArray |
✓ | ✓ |
isEmail |
✓ | ✓ |
isUrl |
✓ | ✓ |
slugify |
✓ | ✓ |
parseDate |
✓ | ✓ |
startOfDay |
✓ | ✓ |
endOfDay |
✓ | ✓ |
startOfMonth |
✓ | ✓ |
endOfMonth |
✓ | ✓ |
startOfYear |
✓ | ✓ |
endOfYear |
✓ | ✓ |
blobToFile |
✓ | ✗ ¹ |
| Regex constants | ✓ | ✓ |
¹
blobToFilerelies on theFileWeb API, which is not available in Node.js.
Clamps a number between min and max (inclusive).
clamp(5, 0, 10); // → 5
clamp(-1, 0, 10); // → 0
clamp(11, 0, 10); // → 10Rounds a number to the specified number of decimal places. Defaults to 0 (integer rounding).
round(3.14159); // → 3
round(3.14159, 2); // → 3.14
round(3.14159, 4); // → 3.1416
round(1.005, 2); // → 1.01Splits an array into chunks of the specified size. The last chunk may be smaller than size.
chunkArray([1, 2, 3, 4, 5], 2);
// → [[1, 2], [3, 4], [5]]Throws if size is less than 1.
Groups array items by a field, returning a record that maps each key to all matching items.
const users = [
{ role: 'admin', name: 'Alice' },
{ role: 'user', name: 'Bob' },
{ role: 'admin', name: 'Carol' },
];
groupByArray(users, 'role');
// → { admin: [{ role: 'admin', name: 'Alice' }, { role: 'admin', name: 'Carol' }], user: [...] }Converts an array into a keyed record. When multiple items share the same key, the last one wins.
const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
mapArray(users, 'id');
// → { '1': { id: 1, name: 'Alice' }, '2': { id: 2, name: 'Bob' } }Returns a new array with the element at the given index removed. Supports negative indices (-1 removes the last element).
removeFromArray(['a', 'b', 'c'], 1); // → ['a', 'c']
removeFromArray(['a', 'b', 'c'], -1); // → ['a', 'b']Throws if the index is out of bounds. Never mutates the original array.
Sorts an array by one or more criteria. Supports string, number, boolean, and Date values. Multiple criteria act as tiebreakers applied in order.
import type { SortCriteria } from '@stanvanheumen/web-utils';
sortArray(users, { key: 'name' });
sortArray(users, [{ key: 'role' }, { key: 'name', direction: 'desc' }]);SortCriteria<T> shape:
| Field | Type | Default |
|---|---|---|
key |
keyof T |
- |
direction |
'asc' or 'desc' |
'asc' |
Adds an item if it is absent, or removes it if it is already present. Accepts an optional custom comparator.
toggleArray([1, 2, 3], 2); // → [1, 3]
toggleArray([1, 3], 2); // → [1, 3, 2]
// Custom comparator
toggleArray(users, { id: 1, name: 'Alice' }, (a, b) => a.id === b.id);Returns true if value is a valid email address. Supports quoted local parts and IP address domains.
isEmail('user@example.com'); // → true
isEmail('not-an-email'); // → falseReturns true if value is a valid URL. Accepts http, https, ftp, and protocol-relative (//) URLs. Rejects private and reserved IP ranges.
isUrl('https://example.com/path?q=1'); // → true
isUrl('http://192.168.1.1'); // → false (private IP)
isUrl('example.com'); // → false (no protocol)Converts a string into a URL-friendly slug. Normalises accented characters, lowercases, and replaces separators with hyphens. Passes null and undefined through unchanged.
slugify('Hello, World!'); // → 'hello-world'
slugify('Ångström Units'); // → 'angstrom-units'
slugify(null); // → nullParses a value into a Date, returning null if the value cannot be interpreted as a valid date. Accepts null and undefined (both return null), Date objects, Unix millisecond timestamps, and ISO 8601 strings.
Supported string formats:
| Format | Example | Timezone |
|---|---|---|
YYYY-MM-DD |
2024-06-15 |
Local midnight |
YYYY-MM-DDTHH:mm:ss |
2024-06-15T12:00:00 |
Treated as UTC |
YYYY-MM-DDTHH:mm:ssZ |
2024-06-15T12:00:00Z |
UTC |
YYYY-MM-DDTHH:mm:ss.sss |
2024-06-15T12:00:00.000 |
Treated as UTC |
YYYY-MM-DDTHH:mm:ss.sssZ |
2024-06-15T12:00:00.000Z |
UTC |
YYYY-MM-DDTHH:mm:ss±HH:mm |
2024-06-15T12:00:00+02:00 |
Offset applied |
Any other string returns null.
parseDate('2024-06-15'); // → Date (local midnight)
parseDate('2024-06-15T12:00:00Z'); // → Date (UTC noon)
parseDate('2024-06-15T12:00:00.000+02:00'); // → Date (with offset)
parseDate(1718449200000); // → Date from timestamp
parseDate('2024-99-99'); // → null (invalid calendar date)
parseDate('June 15 2024'); // → null (unrecognised format)
parseDate(null); // → nullReturns a new Date set to midnight (00:00:00.000) in local time. Does not mutate the original.
startOfDay(new Date('2024-06-15T14:30:00')); // → 2024-06-15T00:00:00.000Returns a new Date set to 23:59:59.999 in local time. Does not mutate the original.
endOfDay(new Date('2024-06-15T08:00:00')); // → 2024-06-15T23:59:59.999Returns a new Date set to the first day of the month at midnight in local time. Does not mutate the original.
startOfMonth(new Date('2024-06-15T14:30:00')); // → 2024-06-01T00:00:00.000Returns a new Date set to the last day of the month at 23:59:59.999 in local time. Does not mutate the original.
endOfMonth(new Date('2024-06-15T14:30:00')); // → 2024-06-30T23:59:59.999
endOfMonth(new Date('2024-02-10T00:00:00')); // → 2024-02-29T23:59:59.999 (leap year)Returns a new Date set to January 1st at midnight in local time. Does not mutate the original.
startOfYear(new Date('2024-06-15T14:30:00')); // → 2024-01-01T00:00:00.000Returns a new Date set to December 31st at 23:59:59.999 in local time. Does not mutate the original.
endOfYear(new Date('2024-06-15T14:30:00')); // → 2024-12-31T23:59:59.999Converts a Blob into a File with the given filename and optional metadata.
const file1 = blobToFile(blob, 'photo.jpg');
const file2 = blobToFile(blob, 'photo.jpg', { type: 'image/jpeg', lastModified: Date.now() });BlobToFileOptions:
| Field | Type | Default |
|---|---|---|
type |
string |
blob.type or '' |
lastModified |
number |
Date.now() |
Pre-compiled regex patterns exported for direct use.
| Export | Matches |
|---|---|
REGEX_EMAIL |
Valid email addresses |
REGEX_URL |
Valid URLs (rejects private IP ranges) |
REGEX_INTEGER |
Integer strings - no leading zeros, no decimals |
import { REGEX_EMAIL, REGEX_URL, REGEX_INTEGER } from '@stanvanheumen/web-utils';
REGEX_EMAIL.test('user@example.com'); // → true
REGEX_INTEGER.test('042'); // → false (leading zero)