Skip to content

Repository files navigation

@mikespa/kit

CInpm versioncodecovminzipped size

A small, framework-agnostic toolkit. Built to stop copy-pasting the same formatCHF / slugify / clamp functions between Next.js projects.

No runtime dependencies. Tree-shakeable via subpath exports.

Install

npm install @mikespa/kit

Usage

Import only what you need via subpaths — this keeps bundles small and is self-documenting at the call site.

import{formatDate,formatCHF,formatSecs,formatHours}from'@mikespa/kit/format';import{slugify,truncate}from'@mikespa/kit/string';import{clamp,timeDiffHours}from'@mikespa/kit/math';formatDate('2026-06-19');// "19.06.2026"formatCHF(1234.5);// "1'234.50 CHF"formatSecs(9000);// "2h30"formatHours(2.5);// "2h30"slugify('Hello, World!');// "hello-world"clamp(15,0,10);// 10timeDiffHours('08:00','10:30');// 2.5

Or import everything from the root if you don't care about tree-shaking:

import{formatCHF,slugify,clamp}from'@mikespa/kit';

Modules

./format

FunctionDescriptionDefault output
formatDate(input)Date as DD.MM.YYYY"19.06.2026"
formatTime(input)Time (24h)"15:30"
formatDateTime(input)Date + time (locale-formatted)"19 juin 2026, 15:30"
formatDateRange(start, end)Compact date range (locale-formatted)"01.06.2026 – 15.06.2026"
formatRelative(input)Relative to now (locale-formatted)"il y a 3 heures"
isToday(input)Whether date is todaytrue
isPast(input)Whether date is in the pasttrue
isFuture(input)Whether date is in the futurefalse
isDateInRange(input, start, end)Whether date falls within [start, end]true
startOfDay(input)Midnight of the given dateDate
endOfDay(input)23:59:59.999 of the given dateDate
parseSwissDate(input)Parse "DD.MM.YYYY" → ISO string"2026-06-19"
formatNumber(value)Locale-aware thousands"1 234 567"
formatCHF(amount)Swiss CHF, apostrophe sep."1'234.50 CHF"
formatCurrency(value)Intl currency (CHF default)"1 234.50 CHF"
formatCompact(value)Compact notation"1,2 k"
parseCHF(input)Parse Swiss amount → number1234.5
formatPercent(value)Percentage"45,6%"
formatBytes(bytes)Human-readable file size"1.5 KB"
formatPhone(input)Swiss phone number"+41 79 123 45 67"
isValidPhone(input)Validate Swiss phone number shapetrue
maskPhone(input)Mask phone number for safe display"+41 79 *** ** 67"
formatIBAN(input)IBAN with spaces"CH56 0483 5012 3456 7800 9"
isValidIBAN(input)Validate IBAN check digits (any country)true
maskIBAN(input)Mask IBAN for safe display"CH56 **** **** **** **** 9"
formatAHV(input)AHV/OASI number, grouped"756.9217.0769.85"
isValidAHV(input)Validate AHV/OASI check digittrue
maskAHV(input)Mask AHV number for safe display"756...85"
formatQRReference(input)QR-bill/ISR reference, grouped"21 00000 00003 13947 14300 09017"
isValidQRReference(input)Validate QR-bill/ISR check digittrue
formatUID(input)UID/VAT number, grouped"CHE-116.281.710"
isValidUID(input)Validate UID/VAT check digittrue
isValidSwissPostalCode(input)Validate 4-digit postal code rangetrue
formatSecs(seconds, step?)Duration from seconds"2h30" / "45min"
formatMinutes(minutes, step?)Duration from minutes"2h30" / "45min"
formatHours(hours, step?)Duration from decimal hours"2h30" / "45min"
formatHoursDecimal(hours)Decimal duration from hours"2.50h"
formatMinutesDecimal(minutes)Decimal duration from minutes"45.00min"
formatSecsDecimal(seconds)Decimal duration from seconds"90.00s"
parseDurationInput(input)Parse "2h30" / "2.5" → minutes150

All format* functions that wrap Intl accept an optional locale parameter (defaults to 'fr-CH') so consuming projects can override per-call.

For apps with a variable/per-request locale (e.g. a language switcher, or SSR with per-request locale), createFormatters(locale) returns those same functions pre-bound to one locale, so call sites don't repeat { locale } everywhere. It's a plain factory — no shared/global state, safe under concurrent requests:

import{createFormatters}from'@mikespa/kit/format';constfmt=createFormatters('de-CH');fmt.formatDate(order.date);fmt.formatCurrency(order.total);

createFormatters binds: formatDate, formatTime, formatDateTime, formatRelative, formatNumber, formatCompact, formatCurrency, formatPercent.

./string

FunctionDescription
slugify(input)URL-friendly slug
truncate(input, maxLength)Truncate with suffix
capitalize(input)First letter uppercase
pluralize(count, singular)Simple pluralizer

./math

FunctionDescription
clamp(value, min, max)Clamp to range
lerp(start, end, t)Linear interpolate
round(value, decimals)Round to N decimals
timeDiffHours(from, to, overnight?)Hours between "HH:MM" strings

./array

FunctionDescription
groupBy(arr, keyFn)Group items into a Record by key
uniqueBy(arr, keyFn)Deduplicate by derived key
sortBy(arr, keyFn, dir?)Sort by derived key, asc or desc

./object

FunctionDescription
pick(obj, keys)New object with only specified keys
omit(obj, keys)New object with specified keys removed

Development

npm install
npm run dev # build in watch mode
npm run build # build once (output -> dist/)
npm test# run tests
npm run test:watch
npm run typecheck

License

MIT

About

A small, framework-agnostic toolkit. Built to stop copy-pasting the same formatCHF / slugify / clamp functions between Next.js projects. No runtime dependencies. Tree-shakeable via subpath exports.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages