# Riyal — Full LLM Context (v1.2.0)
> Complete API reference for the `riyal` npm package. Use this file to understand every export,
> every prop, every code sample, and idiomatic usage patterns before generating code.
- Package: `riyal` (npm, MIT)
- Version: 1.2.0
- Source: https://github.com/pooyagolchian/riyal
- Demo: https://riyal.js.org
- Node ≥ 20, ESM + CJS + TypeScript declarations (`noUncheckedIndexedAccess: true`)
- Author: Pooya Golchian
- New in v1.2: Vue 3 entry, Svelte 5 entry, masked `RiyalInput`, `riyal/cart` checkout primitives.
---
## 1. Background
SAMA (Saudi Central Bank) released the official Saudi Riyal symbol in February 2025. It was assigned to Unicode codepoint **U+20C1** in Unicode 17.0 (September 2025). As of 2026 most operating system fonts do not yet ship this glyph, so `riyal` provides:
1. Real **WOFF2/WOFF/TTF** files generated from the SAMA master SVG (since v1.1.0) — weights Regular/Medium/Bold, variants sans/serif/mono/arabic, all mapped to U+20C1.
2. **Inline-SVG** React, React Native, and Web Component renderers (no font required, work before font loads).
3. **Locale-aware** `formatRiyal` using `Intl.NumberFormat` — symbol always left of number per SAMA placement rules, Arabic-Indic digits in `ar-SA`.
4. **Saudi VAT** helpers at the 15% default rate (customisable per call).
5. **SAR-base currency conversion** with 1-hour in-memory cache using the open.er-api.com free endpoint.
6. **OG image generators** for social-share pricing cards.
---
## 2. Installation
```bash
npm install riyal
pnpm add riyal
yarn add riyal
bun add riyal
```
Peer dependencies (all optional, only needed for the entry you import):
| Entry | Peer |
| --- | --- |
| `riyal/react` | `react ≥ 18`, `react-dom ≥ 18` |
| `riyal/vue` | `vue ≥ 3.4` |
| `riyal/svelte` | `svelte ≥ 5` |
| `riyal/react-native` | `react-native ≥ 0.72`, `react-native-svg ≥ 13` |
| `riyal/tailwind` | `tailwindcss ≥ 3` |
| `riyal/next` | `next ≥ 13` |
### shadcn registry — *new in v1.2*
Production-ready Tailwind components built on top of `riyal/react` + `riyal/cart`,
installable with the shadcn CLI. Each command drops a `.tsx` file into
`components/riyal/` in the consumer's project and adds `riyal` as an npm
dependency.
```bash
# Tailwind-styled SAR price tag (size + tone variants)
npx shadcn@latest add https://riyal.js.org/r/riyal-price-tag.json
# Form-grade SAR amount input (label, hint, error, masked editing)
npx shadcn@latest add https://riyal.js.org/r/riyal-amount-input.json
# Receipt-style cart summary (subtotal, VAT, shipping, grand total)
npx shadcn@latest add https://riyal.js.org/r/riyal-checkout-summary.json
```
Registry index: `https://riyal.js.org/r/registry.json`.
---
## 3. Constants (`riyal`)
```ts
import {
RIYAL_SYMBOL_TEXT, // "" — the actual Unicode character
RIYAL_UNICODE, // "U+20C1" — human-readable codepoint string
RIYAL_CODEPOINT, // 0x20C1 — numeric codepoint
RIYAL_HTML_ENTITY, // "" — for HTML templates
RIYAL_CSS_CONTENT, // "\\20C1" — for CSS content property
RIYAL_CURRENCY_CODE, // "SAR"
RIYAL_ARABIC_ABBREVIATION,// "ر.س"
RIYAL_DEFAULT_LOCALE, // "en-SA"
RIYAL_RTL_LOCALE, // "ar-SA"
SAUDI_VAT_RATE, // 0.15
} from "riyal";
```
---
## 4. Formatting (`riyal`)
### `formatRiyal(amount, options?): string`
```ts
import { formatRiyal } from "riyal";
formatRiyal(2499.99)
// → " 2,499.99" (en-SA default)
formatRiyal(2499.99, { locale: "ar-SA" })
// → " ٢٬٤٩٩٫٩٩" (RTL, Arabic-Indic digits)
formatRiyal(2499.99, { decimals: 0 })
// → " 2,500"
formatRiyal(1500000, { notation: "compact" })
// → " 1.5M"
formatRiyal(2499.99, { symbol: "ر.س" })
// → "ر.س 2,499.99" (Arabic abbreviation override)
```
**`FormatRiyalOptions`:**
| Option | Type | Default |
| --- | --- | --- |
| `locale` | `string` | `"en-SA"` |
| `decimals` | `number` | `2` |
| `symbol` | `string` | `""` |
| `position` | `"prefix" \| "suffix"` | locale-derived (always prefix per SAMA) |
| `notation` | `"standard" \| "compact"` | `"standard"` |
| `groupSeparator` | `string` | locale default |
| `decimalSeparator` | `string` | locale default |
### `parseRiyal(input): number`
Reverses `formatRiyal`. Handles Arabic-Indic digits, compact suffixes (K, M, B, T), the U+20C1 symbol, SAR code, and "ر.س". Returns a plain `number`, throws on unrecognisable input.
```ts
import { parseRiyal } from "riyal";
parseRiyal(" 2,499.99") // → 2499.99
parseRiyal("SAR 2,500") // → 2500
parseRiyal("٢٬٤٩٩٫٩٩ ") // → 2499.99 (Arabic digits)
parseRiyal("1.5M ") // → 1500000 (compact suffix)
parseRiyal("99.90 ر.س") // → 99.9
```
### `maskRiyal(input, caret?, options?)` — *new in v1.2*
Pure helper that powers `RiyalInput` masked mode. Format-as-you-type with paste cleanup,
Arabic-numeral normalisation, thousand-separator grouping, and caret tracking. Returns
`{ value: number, display: string, caret: number }`.
```ts
import { maskRiyal, normalizeRiyalDigits, cleanRiyalString } from "riyal";
maskRiyal("12345"); // { value: 12345, display: "12,345", caret: 6 }
maskRiyal("SAR 2,499.99"); // { value: 2499.99, display: "2,499.99", caret: 8 }
maskRiyal(" 2,499.99"); // { value: 2499.99, display: "2,499.99", ... }
maskRiyal("٢٤٩٩٫٩٩"); // { value: 2499.99, display: "2,499.99", ... }
maskRiyal("99.90 ر.س"); // { value: 99.9, display: "99.90", ... }
maskRiyal("1.23456", undefined, { decimals: 2 }); // { value: 1.23, display: "1.23", ... }
maskRiyal("-1234", undefined, { allowNegative: true }); // { value: -1234, display: "-1,234", ... }
normalizeRiyalDigits("٢٤٩٩"); // "2499" (Arabic-Indic + Persian-Indic digits → ASCII)
cleanRiyalString(" 1,234"); // "1,234" (strips /SAR/ر.س/RTL marks/whitespace)
```
`MaskRiyalOptions = { decimals?: number; allowNegative?: boolean }`. Default decimals
is `2`, default `allowNegative` is `false`.
---
## 5. VAT helpers (`riyal`)
```ts
import { addVAT, removeVAT, getVAT, SAUDI_VAT_RATE } from "riyal";
addVAT(1000) // → 1150 (net × 1.15)
addVAT(1000, { rate: 0.05 }) // → 1050 (UAE 5% override)
removeVAT(1150) // → 1000 (gross ÷ 1.15)
removeVAT(1150, { rate: 0.05 }) // → 1095.24
getVAT(1000) // → 150 (VAT portion only)
getVAT(1000, { rate: 0.05 }) // → 50
SAUDI_VAT_RATE // → 0.15
```
---
## 6. Currency conversion (`riyal`)
```ts
import { fetchExchangeRates, convertFromSAR, convertToSAR } from "riyal";
// Fetch and cache rates (SAR as base, 1-hour in-memory cache)
const rates = await fetchExchangeRates();
// → { USD: 0.267, EUR: 0.245, AED: 0.98, ... }
// Convert from SAR to another currency
const usd = await convertFromSAR(1000, "USD"); // uses cached rates
const aed = await convertFromSAR(1000, "AED", { rate: 0.98 }); // bypass network
// Convert to SAR from another currency
const sar = await convertToSAR(100, "USD");
```
**Error handling:** `fetchExchangeRates` throws `TypeError` on network failure. `convertFromSAR`/`convertToSAR` throw `RangeError` for unknown currency codes.
```ts
let usd: number;
try {
usd = await convertFromSAR(1000, "USD");
} catch {
usd = 1000 * 0.267; // last-known SAR/USD fallback
}
```
---
## 7. Clipboard (`riyal`)
```ts
import { copyRiyalSymbol, copyRiyalAmount } from "riyal";
await copyRiyalSymbol(); // copies ""
await copyRiyalSymbol("html"); // copies ""
await copyRiyalSymbol("css"); // copies "\\20C1"
await copyRiyalAmount(2499.99); // copies " 2,499.99" (en-SA)
await copyRiyalAmount(2499.99, { locale: "ar-SA" }); // ar-SA format
```
Uses `navigator.clipboard.writeText` in browsers; CLI wrapper for server.
---
## 8. React (`riyal/react`)
```tsx
import {
RiyalSymbol,
RiyalIcon,
RiyalPrice,
AnimatedRiyalPrice,
RiyalInput,
useRiyalRate,
} from "riyal/react";
```
### ``
Inline SVG glyph. SSR-safe. No font required.
| Prop | Type | Default |
| --- | --- | --- |
| `size` | `number \| string` | `"1em"` |
| `weight` | `number` | `400` |
| `className` | `string` | — |
```tsx
```
### ``
Standalone SVG `