@foundrykit/utils
v1.0.6
Published
A collection of utility functions and helpers for common development tasks. Provides type-safe utilities for class names, validation, formatting, and more.
Readme
@foundrykit/utils
A collection of utility functions and helpers for common development tasks. Provides type-safe utilities for class names, validation, formatting, and more.
Features
- Type-safe utilities - Full TypeScript support with comprehensive type definitions
- Zero dependencies - Lightweight with no external dependencies
- Tree-shakable - Only import what you need
- Well-tested - Comprehensive test coverage
- Performance optimized - Efficient implementations
Installation
pnpm add @foundrykit/utilsAvailable Utilities
Class Name Utilities
- cn - Conditional class name utility (clsx + tailwind-merge)
- cva - Class variance authority for component variants
Validation Utilities
- isValidEmail - Email validation
- isValidUrl - URL validation
- isValidPhone - Phone number validation
Formatting Utilities
- formatDate - Date formatting utilities
- formatCurrency - Currency formatting
- formatNumber - Number formatting
Array Utilities
- chunk - Split array into chunks
- unique - Remove duplicates from array
- groupBy - Group array items by key
Object Utilities
- pick - Pick specific keys from object
- omit - Omit specific keys from object
- deepMerge - Deep merge objects
Usage
Class Name Utilities
cn (Conditional Class Names)
The primary utility for combining class names with Tailwind CSS:
import { cn } from '@foundrykit/utils';
function Button({ className, variant, ...props }) {
return (
<button
className={cn(
// Base classes
'rounded px-4 py-2 font-medium',
// Conditional classes
variant === 'primary' && 'bg-blue-500 text-white hover:bg-blue-600',
variant === 'secondary' &&
'bg-gray-200 text-gray-900 hover:bg-gray-300',
// User-provided classes (merged intelligently)
className
)}
{...props}
/>
);
}cva (Class Variance Authority)
Create type-safe component variants:
import { cva } from '@foundrykit/utils';
const buttonVariants = cva(
// Base classes
'inline-flex items-center justify-center rounded-md font-medium transition-colors',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive:
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline:
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary:
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
);
function Button({ className, variant, size, ...props }) {
return (
<button
className={cn(buttonVariants({ variant, size }), className)}
{...props}
/>
);
}Validation Utilities
import { isValidEmail, isValidUrl, isValidPhone } from '@foundrykit/utils';
// Email validation
isValidEmail('[email protected]'); // true
isValidEmail('invalid-email'); // false
// URL validation
isValidUrl('https://example.com'); // true
isValidUrl('not-a-url'); // false
// Phone validation
isValidPhone('+1-555-123-4567'); // true
isValidPhone('123'); // falseFormatting Utilities
import { formatDate, formatCurrency, formatNumber } from '@foundrykit/utils';
// Date formatting
formatDate(new Date(), 'MMM dd, yyyy'); // "Jan 15, 2024"
formatDate(new Date(), 'relative'); // "2 hours ago"
// Currency formatting
formatCurrency(1234.56, 'USD'); // "$1,234.56"
formatCurrency(1234.56, 'EUR'); // "€1,234.56"
// Number formatting
formatNumber(1234567); // "1,234,567"
formatNumber(1234.56, { decimals: 2 }); // "1,234.56"Array Utilities
import { chunk, unique, groupBy } from '@foundrykit/utils';
// Split array into chunks
chunk([1, 2, 3, 4, 5, 6], 2); // [[1, 2], [3, 4], [5, 6]]
// Remove duplicates
unique([1, 2, 2, 3, 3, 4]); // [1, 2, 3, 4]
// Group by key
const users = [
{ name: 'Alice', role: 'admin' },
{ name: 'Bob', role: 'user' },
{ name: 'Charlie', role: 'admin' },
];
groupBy(users, 'role');
// {
// admin: [{ name: "Alice", role: "admin" }, { name: "Charlie", role: "admin" }],
// user: [{ name: "Bob", role: "user" }]
// }Object Utilities
import { pick, omit, deepMerge } from '@foundrykit/utils';
const user = {
id: 1,
name: 'John',
email: '[email protected]',
password: 'secret',
};
// Pick specific keys
pick(user, ['id', 'name']); // { id: 1, name: "John" }
// Omit specific keys
omit(user, ['password']); // { id: 1, name: "John", email: "[email protected]" }
// Deep merge objects
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { b: { d: 3 }, e: 4 };
deepMerge(obj1, obj2); // { a: 1, b: { c: 2, d: 3 }, e: 4 }Advanced Usage
Custom Class Name Logic
import { cn } from '@foundrykit/utils';
function StatusBadge({ status, className }) {
return (
<span
className={cn(
'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium',
{
'bg-green-100 text-green-800': status === 'active',
'bg-red-100 text-red-800': status === 'inactive',
'bg-yellow-100 text-yellow-800': status === 'pending',
},
className
)}
>
{status}
</span>
);
}Conditional Rendering with Classes
import { cn } from '@foundrykit/utils';
function Card({ isActive, isDisabled, children, className }) {
return (
<div
className={cn(
'rounded-lg border p-4 transition-all',
isActive && 'border-blue-500 bg-blue-50',
isDisabled && 'cursor-not-allowed opacity-50',
!isActive && !isDisabled && 'hover:border-gray-300',
className
)}
>
{children}
</div>
);
}Performance Considerations
Tree Shaking
Import only what you need:
// Good - only imports cn
import { cn } from '@foundrykit/utils';
// Good - imports multiple utilities
import { cn, cva, isValidEmail } from '@foundrykit/utils';
// Avoid - imports everything
import * as utils from '@foundrykit/utils';Memoization
For expensive operations, consider memoization:
import { useMemo } from 'react';
import { groupBy } from '@foundrykit/utils';
function UserList({ users }) {
const usersByRole = useMemo(() => groupBy(users, 'role'), [users]);
return (
<div>
{Object.entries(usersByRole).map(([role, users]) => (
<div key={role}>
<h3>{role}</h3>
{users.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
))}
</div>
);
}TypeScript Support
All utilities include comprehensive TypeScript definitions:
import { cn, cva } from '@foundrykit/utils';
// Type-safe class name utility
const className: string = cn('base-class', true && 'conditional-class');
// Type-safe variants
const buttonVariants = cva('base', {
variants: {
size: {
sm: 'text-sm',
lg: 'text-lg',
},
},
});
// TypeScript knows the available variants
const buttonClass = buttonVariants({ size: 'sm' }); // ✅ Valid
const invalidClass = buttonVariants({ size: 'xl' }); // ❌ Type errorContributing
When adding new utilities:
- Ensure zero external dependencies
- Add comprehensive TypeScript definitions
- Include JSDoc documentation
- Write unit tests
- Follow existing naming conventions
- Update this README
License
MIT
