npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

svelte-number-format

v1.1.1

Published

Lightweight and reactive input formatting library for Svelte 5. Includes NumericFormat for numbers/currency and PatternFormat for phone, cards, dates, and custom masks.

Readme

svelte-number-format

CI Deploy npm version

Svelte Number Format is a lightweight and reactive input component library for Svelte 5.
Inspired by react-number-format, it provides two powerful components for handling formatted inputs with full caret stability and two-way binding.

Features

Two Specialized Components

  • NumericFormat - Locale-aware number formatting (currency, percentages, decimals)
  • PatternFormat - Pattern-based input masking (phone, credit cards, dates, custom)

🎯 Developer Experience

  • Full TypeScript support
  • Two-way binding with bind:value
  • Svelte 5 native (using runes)
  • Caret position stability
  • Callback hooks for input/change events

🌍 Internationalization

  • Built on Intl.NumberFormat API
  • Support for any locale
  • Automatic formatting based on locale

Live Demo

Check out the working demo: https://pitis.github.io/svelte-number-format/

Installation

npm install svelte-number-format

Quick Start

Currency Input

<script lang="ts">
  import { NumericFormat, NumberFormatStyle } from 'svelte-number-format'

  let amount = $state<number | null>(1234.56)
</script>

<NumericFormat
  bind:value={amount}
  locale="en-US"
  options={{
    formatStyle: NumberFormatStyle.Currency,
    currency: 'USD',
    precision: 2
  }}
  placeholder="$0.00"
/>

Phone Number Input

<script lang="ts">
  import { PatternFormat, MaskPatterns } from 'svelte-number-format'

  let phone = $state<string | null>(null)
</script>

<PatternFormat
  bind:value={phone}
  format={MaskPatterns.PHONE_US}
  placeholder="(123) 456-7890"
/>

NumericFormat Component

Locale-aware number formatting built on intl-number-input.

Props

| Prop | Type | Default | Description | | ---------- | ---------------------------------------------------------- | -------------------- | -------------------------------------------------------------------- | | value | number \| null | null | The numeric value. Use bind:value for two-way binding. | | locale | string | navigator.language | Locale string for formatting (e.g., 'en-US', 'de-DE', 'ja-JP') | | options | Partial<NumberInputOptions> | {} | Formatting options (see below) | | onInput | (raw: number \| null, formatted: string \| null) => void | undefined | Callback fired on every keystroke | | onChange | (raw: number \| null, formatted: string \| null) => void | undefined | Callback fired on blur/change | | ...rest | any | - | All other HTML input attributes (placeholder, class, id, etc.) |

Options

The options prop accepts these properties:

| Option | Type | Description | | ------------------- | -------------------------------- | ----------------------------------------------------------------------------- | | formatStyle | NumberFormatStyle | Decimal, Currency, or Percent | | currency | string | Currency code (e.g., 'USD', 'EUR', 'GBP') - required for Currency style | | precision | number | Number of decimal places | | valueRange | { min?: number, max?: number } | Min/max value constraints | | autoDecimalDigits | boolean | Automatically position decimal (e.g., typing 123412.34) |

NumberFormatStyle Enum

import { NumberFormatStyle } from 'svelte-number-format'

NumberFormatStyle.Decimal // Plain number with locale formatting
NumberFormatStyle.Currency // Currency with symbol ($, €, £, etc.)
NumberFormatStyle.Percent // Percentage (0.75 → 75%)

Examples

Basic Number Input

<script lang="ts">
  import { NumericFormat } from 'svelte-number-format'
  let value = $state<number | null>(1234.56)
</script>

<NumericFormat
  bind:value
  options={{ precision: 2 }}
  placeholder="Enter amount"
/>
<!-- User sees: 1,234.56 -->

Currency (USD)

<script lang="ts">
  import { NumericFormat, NumberFormatStyle } from 'svelte-number-format'
  let price = $state<number | null>(99.99)
</script>

<NumericFormat
  bind:value={price}
  locale="en-US"
  options={{
    formatStyle: NumberFormatStyle.Currency,
    currency: 'USD',
    precision: 2
  }}
/>
<!-- User sees: $99.99 -->

Currency (EUR with German locale)

<NumericFormat
  bind:value={amount}
  locale="de-DE"
  options={{
    formatStyle: NumberFormatStyle.Currency,
    currency: 'EUR',
    precision: 2
  }}
/>
<!-- User sees: 1.234,56 € -->

Percentage

<script lang="ts">
  import { NumericFormat, NumberFormatStyle } from 'svelte-number-format'
  let rate = $state<number | null>(0.75) // Store as decimal
</script>

<NumericFormat
  bind:value={rate}
  options={{
    formatStyle: NumberFormatStyle.Percent,
    precision: 2
  }}
/>
<!-- User sees: 75.00% -->
<!-- Value stored as: 0.75 -->

With Value Range

<NumericFormat
  bind:value={amount}
  options={{
    precision: 2,
    valueRange: { min: 0, max: 1000 }
  }}
  placeholder="0 - 1000"
/>
<!-- Values are clamped to 0-1000 on blur -->

Auto Decimal Mode

<NumericFormat
  bind:value={price}
  options={{
    precision: 2,
    autoDecimalDigits: true
  }}
  placeholder="Type 1234 → 12.34"
/>
<!-- Typing "1234" automatically formats as "12.34" -->

With Callbacks

<script lang="ts">
  import { NumericFormat } from 'svelte-number-format'

  let value = $state<number | null>(null)

  function handleInput(raw: number | null, formatted: string | null) {
    console.log('Input:', raw, formatted)
  }

  function handleChange(raw: number | null, formatted: string | null) {
    console.log('Change:', raw, formatted)
  }
</script>

<NumericFormat
  bind:value
  options={{ precision: 2 }}
  onInput={handleInput}
  onChange={handleChange}
/>

PatternFormat Component

Pattern-based input masking for structured text inputs.

Props

| Prop | Type | Default | Description | | ------------- | ---------------------------------------------------------- | ----------- | ------------------------------------------------------------------------ | | value | string \| null | null | The raw unmasked value. Use bind:value for two-way binding. | | format | string | '' | Pattern string (e.g., '(###) ###-####'). See pattern characters below. | | mask | string | '' | Deprecated - Use format instead. Kept for backwards compatibility. | | maskChar | string | '_' | Character shown in placeholder for pattern positions | | placeholder | string | auto | Placeholder text (auto-generated from format if not provided) | | onInput | (raw: string \| null, formatted: string \| null) => void | undefined | Callback fired on every keystroke | | onChange | (raw: string \| null, formatted: string \| null) => void | undefined | Callback fired on blur/change | | ...rest | any | - | All other HTML input attributes |

Pattern Characters

| Character | Accepts | Example | | --------- | ------------------------ | ----------------------------- | | # | Digit (0-9) | ###123 | | A | Letter (a-zA-Z) | AAAABC | | * | Alphanumeric (a-zA-Z0-9) | ***A1B | | Other | Literal | -, (, ), /, :, etc. |

Predefined Patterns

Import ready-to-use patterns:

import { MaskPatterns } from 'svelte-number-format'

Phone Numbers

MaskPatterns.PHONE_US // (###) ###-####
MaskPatterns.PHONE_US_WITH_EXT // (###) ###-#### ext. #####
MaskPatterns.PHONE_INTERNATIONAL // +## (###) ###-####

Credit Cards

MaskPatterns.CREDIT_CARD // #### #### #### ####
MaskPatterns.CREDIT_CARD_AMEX // #### ###### #####

Dates & Time

MaskPatterns.DATE_US // ##/##/####
MaskPatterns.DATE_ISO // ####-##-##
MaskPatterns.DATE_EU // ##.##.####
MaskPatterns.TIME_12H // ##:## AM
MaskPatterns.TIME_24H // ##:##
MaskPatterns.DATETIME_US // ##/##/#### ##:##

Identification

MaskPatterns.SSN // ###-##-####
MaskPatterns.ZIP_US // #####
MaskPatterns.ZIP_US_PLUS4 // #####-####

Other

MaskPatterns.IPV4 // ###.###.###.###
MaskPatterns.MAC_ADDRESS // ##:##:##:##:##:##
MaskPatterns.HEX_COLOR // #******

Examples

Phone Number

<script lang="ts">
  import { PatternFormat, MaskPatterns } from 'svelte-number-format'
  let phone = $state<string | null>(null)
</script>

<PatternFormat bind:value={phone} format={MaskPatterns.PHONE_US} />
<!-- User types: 1234567890 -->
<!-- Display: (123) 456-7890 -->
<!-- Value stored: "1234567890" -->

Credit Card

<script lang="ts">
  import { PatternFormat, MaskPatterns } from 'svelte-number-format'
  let card = $state<string | null>(null)
</script>

<PatternFormat
  bind:value={card}
  format={MaskPatterns.CREDIT_CARD}
  placeholder="1234 5678 9012 3456"
/>
<!-- User types: 1234567890123456 -->
<!-- Display: 1234 5678 9012 3456 -->
<!-- Value stored: "1234567890123456" -->

Date

<PatternFormat
  bind:value={date}
  format={MaskPatterns.DATE_US}
  placeholder="MM/DD/YYYY"
/>
<!-- User types: 12252024 -->
<!-- Display: 12/25/2024 -->
<!-- Value stored: "12252024" -->

Social Security Number

<PatternFormat bind:value={ssn} format={MaskPatterns.SSN} />
<!-- Display: 123-45-6789 -->
<!-- Value stored: "123456789" -->

Custom Pattern

<PatternFormat
  bind:value={code}
  format="AAA-###-***"
  placeholder="ABC-123-XYZ"
/>
<!-- Accepts: [Letter][Letter][Letter]-[Digit][Digit][Digit]-[Any][Any][Any] -->
<!-- Example: ABC-123-X5Z -->
<!-- Value stored: "ABC123X5Z" -->

License Plate (Custom)

<PatternFormat bind:value={plate} format="AAA ####" placeholder="ABC 1234" />

Product Code (Custom)

<PatternFormat bind:value={product} format="***-***-***" />
<!-- Accepts any combination of letters and numbers -->

Advanced Usage

Controlled Components

Both components support controlled mode:

<script lang="ts">
  import { NumericFormat } from 'svelte-number-format'
  let amount = $state<number | null>(100)
</script>

<NumericFormat bind:value={amount} options={{ precision: 2 }} />

<button onclick={() => (amount = 100)}>$100</button>
<button onclick={() => (amount = 1000)}>$1,000</button>
<button onclick={() => (amount = null)}>Clear</button>

Form Integration

<script lang="ts">
  let formData = $state({
    price: null as number | null,
    phone: null as string | null
  })

  function handleSubmit() {
    console.log('Form data:', formData)
  }
</script>

<form onsubmit={handleSubmit}>
  <NumericFormat
    bind:value={formData.price}
    options={{ formatStyle: NumberFormatStyle.Currency, currency: 'USD' }}
  />

  <PatternFormat bind:value={formData.phone} format={MaskPatterns.PHONE_US} />

  <button type="submit">Submit</button>
</form>

Custom Styling

<NumericFormat
  bind:value={amount}
  class="my-custom-input"
  style="border: 2px solid blue;"
/>

<style>
  :global(.my-custom-input) {
    padding: 1rem;
    font-size: 1.5rem;
    border-radius: 8px;
  }
</style>

Migration from v1.x

If you're upgrading from an earlier version, see MIGRATION.md for the full migration guide.

Quick Migration

Old names (still work):

import {(SvelteNumberFormat, SvelteMaskFormat)} from 'svelte-number-format';

New names (recommended):

import {(NumericFormat, PatternFormat)} from 'svelte-number-format';

TypeScript

Full TypeScript support with proper type definitions:

import type { NumberInputOptions } from 'intl-number-input'
import {
  NumericFormat,
  PatternFormat,
  NumberFormatStyle,
  MaskPatterns
} from 'svelte-number-format'
import type { MaskPattern } from 'svelte-number-format'

Browser Support

  • Svelte 5+
  • Modern browsers with Intl.NumberFormat support
  • IE11+ with polyfills

Contributing

Contributions are welcome! This project uses:

  • Husky - Git hooks for quality checks
  • lint-staged - Run checks on staged files only
  • Pre-commit hooks - Automatic formatting, linting, and testing

Before each commit, the following runs automatically:

  • ✅ Prettier formatting
  • ✅ ESLint linting with auto-fix
  • ✅ Tests for changed files

See CONTRIBUTING.md for detailed development setup and guidelines.


License

MIT © Pitis Radu


Acknowledgments