A powerful, reactive number formatter utility for Svelte + TypeScript with comprehensive validation, real-time formatting, and extensive customization options.
12,345.67
, $1,000.00
, 85%
, etc.)npm install svelte-number-formatter
npm install @dev-henen/svelte-number-formatter --registry=https://npm.pkg.github.com
š GitHub Repository: github.com/dev-henen/svelte-number-formatter
import { NumberFormatter } from 'svelte-number-formatter';
const formatter = new NumberFormatter();
<script lang="ts">
import { NumberFormatter } from 'svelte-number-formatter';
const formatter = new NumberFormatter();
</script>
<input bind:value={formatter.handler} />
<p>Raw: {formatter.raw}</p>
<p>Formatted: {formatter.formatted}</p>
<p>Numeric Value: {formatter.value}</p>
or, using a local value:
<script lang="ts">
import { NumberFormatter } from 'svelte-number-formatter';
const formatter = new NumberFormatter();
let value = formatter.formatted;
$: formatter.handler = value;
</script>
<input bind:value={value} />
<p>Raw: {formatter.raw}</p>
<p>Formatted: {formatter.formatted}</p>
new NumberFormatter(initial?, options?)
Create a new formatter with optional initial value and formatting options.
const formatter = new NumberFormatter("1234.56", {
style: 'currency',
currency: 'USD',
decimals: 2,
useGrouping: true
});
Property | Type | Description |
---|---|---|
handler |
string |
Input setter that auto-updates raw + formatted |
raw |
string |
The numeric value stripped of symbols |
formatted |
string |
The value formatted for display (12,345.67 ) |
value |
number |
The parsed numeric value |
Store | Type | Description |
---|---|---|
validation |
Readable<ValidationResult> |
Current validation state |
isEditing |
Readable<boolean> |
Whether input is currently being edited |
numericValue |
Readable<number> |
Parsed numeric value as store |
š Setting
.handler = "12345"
will automatically update.raw
,.formatted
, and.value
.
setOptions(options: Partial<FormatOptions>)
Update formatting options dynamically:
formatter.setOptions({
style: 'currency',
currency: 'EUR',
decimals: 2,
min: 0,
max: 10000
});
type FormatOptions = {
locale?: string; // e.g., "en-US", "de-DE"
useGrouping?: boolean; // thousand separators (default: true)
decimals?: number; // decimal places (default: 0)
currency?: string; // e.g., "USD", "EUR"
style?: "decimal" | "currency" | "percent"; // formatting style
allowNegative?: boolean; // allow negative numbers (default: true)
allowDecimal?: boolean; // allow decimal input (default: true)
min?: number; // minimum value
max?: number; // maximum value
prefix?: string; // custom prefix
suffix?: string; // custom suffix
placeholder?: string; // placeholder for empty values
strict?: boolean; // reject invalid input (default: false)
};
format(value: string | number): string
Format a raw number programmatically:
formatter.format("123456.789"); // ā "123,456.79" or "$123,456.79"
setValue(value: string | number): void
Set the value programmatically:
formatter.setValue(1234.56);
reset(): void
Clear all values and reset validation state:
formatter.reset();
isValid(): boolean
Check if current value is valid:
if (formatter.isValid()) {
// proceed with valid value
}
getValidation(): ValidationResult
Get detailed validation information:
const validation = formatter.getValidation();
console.log(validation.isValid, validation.error, validation.warning);
increment(step?: number): void
Increment the current value:
formatter.increment(1); // +1
formatter.increment(10); // +10
decrement(step?: number): void
Decrement the current value:
formatter.decrement(1); // -1
formatter.decrement(5); // -5
focus(): void
/ blur(): void
Simulate focus/blur for form integration:
formatter.focus(); // sets isEditing to true
formatter.blur(); // sets isEditing to false
Subscribe to all formatter state changes:
const unsubscribe = formatter.subscribe(({ raw, formatted, value, validation, isEditing }) => {
console.log("Raw:", raw);
console.log("Formatted:", formatted);
console.log("Value:", value);
console.log("Valid:", validation.isValid);
console.log("Editing:", isEditing);
});
subscribeFormatted(run: Subscriber<string>): Unsubscriber
const unsubscribe = formatter.subscribeFormatted(formatted => {
console.log("Formatted:", formatted);
});
subscribeRaw(run: Subscriber<string>): Unsubscriber
const unsubscribe = formatter.subscribeRaw(raw => {
console.log("Raw:", raw);
});
subscribeValue(run: Subscriber<number>): Unsubscriber
const unsubscribe = formatter.subscribeValue(value => {
console.log("Value:", value);
});
The formatter includes a comprehensive validation system:
type ValidationResult = {
isValid: boolean;
error?: string; // Validation error message
warning?: string; // Warning message (non-blocking)
};
<script lang="ts">
import { NumberFormatter } from 'svelte-number-formatter';
const formatter = new NumberFormatter("", {
min: 0,
max: 100,
decimals: 2,
strict: true
});
let validation = formatter.getValidation();
$: validation = $formatter.validation;
</script>
<input bind:value={formatter.handler} />
{#if !validation.isValid}
<p class="error">{validation.error}</p>
{/if}
{#if validation.warning}
<p class="warning">{validation.warning}</p>
{/if}
// US Dollar
const usdFormatter = new NumberFormatter("1234.56", {
style: 'currency',
currency: 'USD',
locale: 'en-US'
});
// ā "$1,234.56"
// Euro
const eurFormatter = new NumberFormatter("1234.56", {
style: 'currency',
currency: 'EUR',
locale: 'de-DE'
});
// ā "1.234,56 ā¬"
const percentFormatter = new NumberFormatter("0.85", {
style: 'percent',
decimals: 1
});
// ā "85.0%"
const customFormatter = new NumberFormatter("42", {
prefix: "Score: ",
suffix: " pts",
useGrouping: false
});
// ā "Score: 42 pts"
<script lang="ts">
import { NumberFormatter } from 'svelte-number-formatter';
const formatter = new NumberFormatter("0", {
min: 0,
max: 100,
decimals: 0
});
</script>
<div class="stepper">
<button on:click={() => formatter.decrement()}>-</button>
<input bind:value={formatter.handler} />
<button on:click={() => formatter.increment()}>+</button>
</div>
<p>Value: {formatter.value}</p>
<script lang="ts">
import { NumberFormatter } from 'svelte-number-formatter';
const priceFormatter = new NumberFormatter("", {
style: 'currency',
currency: 'USD',
decimals: 2,
min: 0,
max: 999999,
strict: true
});
$: isValid = $priceFormatter.validation.isValid;
</script>
<form>
<label>
Price:
<input
bind:value={priceFormatter.handler}
class:invalid={!isValid}
/>
</label>
{#if !isValid}
<span class="error">{$priceFormatter.validation.error}</span>
{/if}
<button type="submit" disabled={!isValid}>
Submit
</button>
</form>
NumberFormatter.formatValue(value, options?): string
Format a value without creating a formatter instance:
const formatted = NumberFormatter.formatValue(1234.56, {
style: 'currency',
currency: 'USD'
});
// ā "$1,234.56"
NumberFormatter.parseValue(formattedValue): number
Parse a formatted value back to a number:
const value = NumberFormatter.parseValue("$1,234.56");
// ā 1234.56
const moneyFormatter = new NumberFormatter("", {
style: 'currency',
currency: 'USD',
decimals: 2,
min: 0
});
const percentFormatter = new NumberFormatter("", {
style: 'percent',
decimals: 1,
min: 0,
max: 1
});
const quantityFormatter = new NumberFormatter("1", {
decimals: 0,
min: 1,
max: 999,
useGrouping: false
});
const scientificFormatter = new NumberFormatter("", {
decimals: 3,
useGrouping: false,
allowNegative: true
});
navigator.language
Intl.NumberFormat
for reliable formattinghandler
acts as a two-way reactive setter for <input>
elementsstrict: true
) rejects invalid input entirelyMIT Ā© dev-henen
Contributions are welcome! Please feel free to submit a Pull Request.