Price display utilities for e-commerce -- recurring interval labels, discount percentage calculation, compare-at price detection. Works with any price object shape (camelCase or snake_case fields).
Zero dependencies. Works in Node.js, Cloudflare Workers, Deno, Bun, and browsers.
npm install @arraypress/price-utilsimport {
isRecurring,
getIntervalLabel,
hasCompareAt,
getDiscountPercent,
getSavingsAmount,
getPriceTypeLabel,
isFree,
} from '@arraypress/price-utils';
// Recurring price detection
isRecurring({ type: 'recurring', recurringInterval: 'month' }); // true
isRecurring({ type: 'one_time' }); // false
// Human-readable interval labels
getIntervalLabel({ recurringInterval: 'month' }); // 'Monthly'
getIntervalLabel({ recurringInterval: 'year' }); // 'Yearly'
getIntervalLabel({ recurringInterval: 'month', recurringIntervalCount: 3 }); // 'Every 3 months'
// Compare-at / sale price
hasCompareAt({ amount: 1500, compareAtAmount: 2000 }); // true
getDiscountPercent({ amount: 1500, compareAtAmount: 2000 }); // 25
getSavingsAmount({ amount: 1500, compareAtAmount: 2000 }); // 500 (cents)
// Price type labels
getPriceTypeLabel({ type: 'recurring', recurringInterval: 'month' }); // 'Subscription'
getPriceTypeLabel({ type: 'one_time' }); // 'One-time'
// Free price detection
isFree({ amount: 0 }); // true
isFree({ amount: 1999 }); // falseCheck if a price is a recurring/subscription price. Accepts both type: 'recurring' and the presence of a recurringInterval / recurring_interval field.
Get a human-readable interval label. Returns 'Monthly', 'Yearly', 'Weekly', 'Daily' for count=1, or 'Every N months/years/weeks/days' for higher counts.
Check if a price has a compare-at (was/original) price higher than the current price.
Calculate the discount percentage (0-100) between compare-at and current price, rounded to the nearest integer.
Calculate the savings amount in cents (compare-at minus current price).
Returns 'Subscription' for recurring prices, 'One-time' for everything else, or '' for null/undefined.
Check if a price is free (amount is 0 or missing).
All functions accept both camelCase and snake_case field names:
| camelCase | snake_case |
|---|---|
recurringInterval |
recurring_interval |
recurringIntervalCount |
recurring_interval_count |
compareAtAmount |
compare_at_amount |
MIT