svelte-currency-input

A Svelte 5 form input with currency masking and internationalization support



Installation

bun add @canutin/svelte-currency-input

Basic usage

float 1234.56
formatted $1,234.56
value "1234.56"
<script lang="ts">
	import { CurrencyInput } from '@canutin/svelte-currency-input';
	let value = $state('1234.56');
</script>

<CurrencyInput
	bind:value
	intlConfig={{ locale: 'en-US', currency: 'USD' }}
/>

International currencies

float 1234.56
formatted 1.234,56 €
value "1234.56"
<script lang="ts">
	import { CurrencyInput } from '@canutin/svelte-currency-input';

	const presets = [
		{ label: 'de-DE (EUR)', intlConfig: { locale: 'de-DE', currency: 'EUR' } },
		{ label: 'ja-JP (JPY)', intlConfig: { locale: 'ja-JP', currency: 'JPY' } },
		{ label: 'en-IN (INR)', intlConfig: { locale: 'en-IN', currency: 'INR' } },
		{ label: 'es-PE (PEN)', intlConfig: { locale: 'es-PE', currency: 'PEN' } },
		{ label: 'es-CR (CRC)', intlConfig: { locale: 'es-CR', currency: 'CRC' } },
		{ label: 'th-TH (THB)', intlConfig: { locale: 'th-TH', currency: 'THB' } },
		{ label: 'he-IL (ILS)', intlConfig: { locale: 'he-IL', currency: 'ILS' } }
	];

	let selectedIndex = $state(0);
	let intlConfig = $derived(presets[selectedIndex].intlConfig);
	let value = $state('1234.56');
</script>

<select bind:value={selectedIndex}>
	{#each presets as preset, i}
		<option value={i}>{preset.label}</option>
	{/each}
</select>

<CurrencyInput
	bind:value
	{intlConfig}
/>

Abbreviations

float null
formatted ""
value ""
<script lang="ts">
	import { CurrencyInput } from '@canutin/svelte-currency-input';
	let value = $state('');
</script>

<!-- Type 1k, 2.5m, or 1b -->
<CurrencyInput
	bind:value
	intlConfig={{ locale: 'en-US', currency: 'USD' }}
/>

Decimal precision

float 99.99
formatted $99.99
value "99.99"
<script lang="ts">
	import { CurrencyInput } from '@canutin/svelte-currency-input';
	let value = $state('99.99');
</script>

<CurrencyInput
	bind:value
	intlConfig={{ locale: 'en-US', currency: 'USD' }}
	decimalsLimit={2}
	decimalScale={2}
/>

Min, max, and step

float 50
formatted $50.00
value "50"
<script lang="ts">
	import { CurrencyInput } from '@canutin/svelte-currency-input';
	let value = $state('50');
</script>

<!-- Use arrow keys to step -->
<CurrencyInput
	bind:value
	intlConfig={{ locale: 'en-US', currency: 'USD' }}
	min={0}
	max={100}
	step={10}
/>

Custom prefix and suffix

float 1500
formatted 1,500 pts
value "1500"
float 0.00042069
formatted ₿ 0.00042069
value "0.00042069"
<CurrencyInput
	suffix=" pts"
	decimalsLimit={0}
/>

<CurrencyInput
	prefix="₿ "
	decimalsLimit={8}
/>

Dynamic styling

float -3.14
formatted -£3.14
value "-3.14"
<script lang="ts">
	import { CurrencyInput } from '@canutin/svelte-currency-input';
	let value = $state('-3.14');

	const getValueColor = (val: string) => {
		if (!val || val === '-') return '';
		const num = parseFloat(val);
		if (num === 0) return 'text-slate-400';
		return num < 0 ? 'text-rose-500' : 'text-emerald-500';
	};
</script>

<CurrencyInput
	bind:value
	intlConfig={{ locale: 'en-GB', currency: 'GBP' }}
	class={getValueColor(value)}
/>

Chained inputs

float 1000
formatted $1,000
value "1000"
float 1000
formatted 1.000 €
value "1000"
float 1000
formatted £1,000
value "1000"
<script lang="ts">
	import { CurrencyInput } from '@canutin/svelte-currency-input';
	let value = $state('1000');
</script>

<CurrencyInput
	bind:value
	intlConfig={{ locale: 'en-US', currency: 'USD' }}
/>

<CurrencyInput
	bind:value
	intlConfig={{ locale: 'de-DE', currency: 'EUR' }}
/>

<CurrencyInput
	bind:value
	intlConfig={{ locale: 'en-GB', currency: 'GBP' }}
	disabled
/>

Format utility

$1,234,567.89
<script lang="ts">
	import { formatValue } from '@canutin/svelte-currency-input';

	let value = $state('1234567.89');
	let prefix = $state('$');
	let groupSeparator = $state(',');
	let decimalSeparator = $state('.');

	let formattedResult = $derived(
		formatValue({
			value,
			prefix,
			groupSeparator,
			decimalSeparator
		})
	);
</script>

{formattedResult}

One USD, many pesos

OficialUS$ 1.495$ 1.495 ARS
MEPUS$ 1.503,6$ 1.503,6 ARS
BlueUS$ 1.530$ 1.530 ARS
CCLUS$ 1.534,9$ 1.534,9 ARS
CriptoUS$ 1.549,72$ 1.549,72 ARS
TarjetaUS$ 1.943,5$ 1.943,5 ARS
<script lang="ts">
	import { CurrencyInput, formatValue } from '@canutin/svelte-currency-input';

	const arsConfig = { locale: 'es-AR', currency: 'ARS' };
	const usdConfig = { locale: 'es-AR', currency: 'USD' };

	const dollarRates = [
		{ name: 'Oficial', rate: 1495 },
		{ name: 'MEP', rate: 1503.6 },
		{ name: 'Blue', rate: 1530 },
		{ name: 'CCL', rate: 1534.9 },
		{ name: 'Cripto', rate: 1549.72 },
		{ name: 'Tarjeta', rate: 1943.5 }
	];

	let usdAmount = $state<number | null>(1);

	function formatUSD(value: number): string {
		return formatValue({ value: value.toFixed(2), intlConfig: usdConfig });
	}

	function formatARS(value: number): string {
		return formatValue({ value: value.toFixed(2), intlConfig: arsConfig });
	}
</script>

<CurrencyInput
	value={usdAmount != null ? String(usdAmount) : ''}
	oninputvalue={(v) => (usdAmount = v.float)}
	intlConfig={usdConfig}
/>

<table>
	{#each dollarRates as { name, rate } (name)}
		<tr>
			<td>{name}</td>
			<td>{formatUSD(rate)}</td>
			<td>{formatARS((usdAmount ?? 0) * rate)}</td>
		</tr>
	{/each}
</table>