Skip to Content
Utils & Extensions

Utils & Extensions (@numa/utils)

Pure helper functions, prototype extensions, and shared React hooks.

Package structure

packages/utils/ ├── cn.ts # clsx + tailwind-merge ├── cookies.ts # Cookie get/set/remove helpers ├── locale.ts # Locale resolution helpers ├── validation.ts # Phone, JWT validation ├── logger.ts # Structured console logger ├── filters.ts # Filtering utilities ├── form-diff.ts # Form diff utilities ├── icon.tsx # Icon helper ├── typography.tsx # Typography helper ├── extensions/ # Prototype extensions │ ├── index.ts # Re-exports all extensions │ ├── string.ts # String.prototype extensions │ ├── number.ts # Number.prototype extensions │ ├── date.ts # Date/string date formatting │ └── duration.ts # Duration formatting └── hooks/ # Shared React hooks ├── use-delayed-show.ts # Deferred UI (e.g. spinner after delay) └── use-page-filters.ts # Page filter state management

Imports

import { cn } from "@numa/utils/cn"; import { getCookie } from "@numa/utils/cookies"; import { useDelayedShow } from "@numa/utils/hooks"; import "@numa/utils/extensions";

Utils vs Extensions

TypeLocationUse Case
UtilsDirect importsPure functions you import and call
Extensionsextensions/Prototype methods on String, Number

Prefer extensions for natural chaining like "2024-01-01".toDate(). Use utils for one-off helpers.

cn() — Class merging

Combines clsx and tailwind-merge for conditional Tailwind classes:

import { cn } from "@numa/utils/cn"; <div className={cn("base-class", condition && "conditional-class")} />
import { getCookie, setCookie, removeCookie } from "@numa/utils/cookies"; const token = getCookie("_numa_access_token_"); setCookie("_numa_selected_locale_", "en"); removeCookie("_numa_access_token_");

Logger

Structured console logger (only file with no-console ESLint disable):

import { logger } from "@numa/utils/logger"; logger.info("User logged in", { userId: "123" }); logger.error("Failed to fetch", error);

Extensions

Extensions must be imported once in the app entry point for their prototype augmentations to apply:

import "@numa/utils/extensions";

This is done in @numa/providers/rq.tsx.

String extensions

MethodDescriptionExample
sanitize()Trim, lowercase, strip spaces" Hello ".sanitize()"hello"
toShort(length?)Truncate with ellipsis"Long text".toShort(4)"Long..."

Number extensions

MethodDescriptionExample
toNumber()Locale-formatted string1234.toNumber()"1,234"
toMoney()Formatted with currency (Rwf)5000.toMoney()"Rwf 5,000"
toKilo()Compact format1500.toKilo()"1.5K"
toUnit(unit)Number with unit suffix5.toUnit("KM")"5 KMs"

Date extensions

Available on String:

MethodDescription
toDate()Formatted date
toDateOnly()Date without time
toDateAndTime()Date with time
toTextDMY()Text date (Day Month Year)
toDuration(endTime)Duration between dates

Hooks

useDelayedShow

Defers showing a UI element (e.g., spinner) by a configurable delay:

const show = useDelayedShow(isLoading, 300); return show ? <Spinner /> : null;

usePageFilters

Manages filter state for paginated list pages:

const { filters, updateFilter, resetFilters } = usePageFilters({ search: "", status: "active", });
Last updated on