Raqam
Recipes

Persian E-commerce

Building number inputs for Persian-language e-commerce — toman currency, RTL, and native digit support.

This recipe shows how to build a complete Persian-language price input that:

  • Accepts Persian digits (۱۲۳) and converts them automatically
  • Displays the toman suffix (تومان)
  • Renders correctly in RTL context
  • Formats with Persian grouping separators (٬)

Setup

// app/layout.tsx or main.tsx
import "raqam/locales/fa";  // Register Persian digit block

Basic toman input

import { NumberField } from "raqam";

export function TomanInput() {
  return (
    <NumberField.Root
      locale="fa-IR"
      suffix=" تومان"
      defaultValue={0}
      minValue={0}
      step={1000}
      largeStep={10000}
    >
      <NumberField.Label>قیمت</NumberField.Label>  {/* "Price" */}
      <NumberField.Group>
        <NumberField.Decrement>−</NumberField.Decrement>
        <NumberField.Input />  {/* raqam applies direction: ltr automatically for RTL locales */}
        <NumberField.Increment>+</NumberField.Increment>
      </NumberField.Group>
    </NumberField.Root>
  );
}

Displays: ۱٬۲۳۴ تومان

Controlled with formatted display

import { useState } from "react";
import { NumberField, useNumberFieldFormat } from "raqam";

export function ProductPriceEditor() {
  const [price, setPrice] = useState<number | null>(125000);

  const formatted = useNumberFieldFormat(price, {
    locale: "fa-IR",
    formatOptions: {},
  });

  return (
    <div dir="rtl" style={{ fontFamily: "Vazirmatn, sans-serif" }}>
      <NumberField.Root
        locale="fa-IR"
        suffix=" تومان"
        value={price}
        onChange={setPrice}
        minValue={1000}
        step={500}
      >
        <NumberField.Label style={{ display: "block", marginBottom: 4 }}>
          قیمت محصول
        </NumberField.Label>
        <NumberField.Group style={{ display: "flex" }}>
          <NumberField.Decrement>−</NumberField.Decrement>
          {/* An inline `style` replaces raqam's RTL style object, so re-declare
              direction: ltr here (or style the input with className instead). */}
          <NumberField.Input
            style={{ flex: 1, padding: "6px 10px", direction: "ltr", textAlign: "right" }}
          />
          <NumberField.Increment>+</NumberField.Increment>
        </NumberField.Group>
        <NumberField.ErrorMessage style={{ color: "red", fontSize: 12 }} />
      </NumberField.Root>

      <p style={{ marginTop: 8, fontSize: 13, color: "#555" }}>
        قیمت نمایش‌داده‌شده: <strong>{formatted} تومان</strong>
      </p>
    </div>
  );
}

Digit input examples

Persian digit normalization is transparent:

User typesraqam storesDisplays
۱۲۳۴ (Persian)1234۱٬۲۳۴
1234 (ASCII)1234۱٬۲۳۴
Mixed ۱2۳41234۱٬۲۳۴

IRR currency format

If you want the official rial currency symbol from Intl.NumberFormat:

<NumberField.Root
  locale="fa-IR"
  formatOptions={{ style: "currency", currency: "IRR" }}
  defaultValue={0}
>
  <NumberField.Input />
</NumberField.Root>
// Displays something like: ۰ ﷼
// The exact symbol (﷼ vs ریال) and number of fraction digits come from the
// runtime's ICU data, so output can vary across environments.

Validation for price ranges

<NumberField.Root
  locale="fa-IR"
  suffix=" تومان"
  defaultValue={50000}
  minValue={10000}
  maxValue={100000000}
  step={1000}
  validate={(v) => {
    if (v === null) return "قیمت الزامی است";
    if (v < 10000) return "حداقل قیمت ۱۰٬۰۰۰ تومان است";
    if (v > 100000000) return "حداکثر قیمت ۱۰۰٬۰۰۰٬۰۰۰ تومان است";
    return true;
  }}
>
  <NumberField.Label>قیمت</NumberField.Label>
  <NumberField.Input />
  <NumberField.ErrorMessage />
</NumberField.Root>

SSR with Next.js

For server-side price display in Persian:

import { createFormatter } from "raqam/server";

const priceFormatter = createFormatter({
  locale: "fa-IR",
  suffix: " تومان",
});

// In a Server Component:
const displayPrice = priceFormatter.format(125000);
// "۱۲۵٬۰۰۰ تومان"

The fa-IR locale uses ٬ (Arabic thousands separator, U+066C) and ٫ (Arabic decimal separator, U+066B) — different from the ASCII , and .. raqam extracts these from Intl.NumberFormat automatically.

On this page