dz-form-components
v1.0.0
Published
shadcn-style React + TypeScript form components for Algerian inputs: WilayaSelect, CommuneSelect, AlgerianPhoneInput, RCInput, NIFInput.
Maintainers
Readme
dz-form-components
🇩🇿 shadcn-style React + TypeScript form components for Algerian inputs. العربية
Accessible, trilingual (AR / FR / EN) form controls with the data and validation rules baked in:
| Component | What it does |
| -------------------- | ----------------------------------------------------------------- |
| WilayaSelect | Dropdown of Algeria's 58 wilayas, trilingual labels. |
| CommuneSelect | Dependent dropdown of communes for the selected wilaya. |
| AlgerianPhoneInput | Phone field with a fixed +213 prefix and live validation. |
| RCInput | Registre du Commerce input (YY/WW-SSSSSSS[ L]) with validation. |
| NIFInput | 15-digit fiscal ID (NIF) input with validation. |
- ✅ 58 wilayas + 1541 communes bundled (trilingual + postal codes).
- ✅ Built on Radix UI primitives — accessible by default.
- ✅ Tailwind v4 styling, shadcn-compatible design tokens (inherits your theme).
- ✅ Full RTL support for Arabic.
- ✅ ESM + CJS, TypeScript types, React 18 & 19.
Install
npm install dz-form-components
# peer deps (you almost certainly already have these)
npm install react react-domStyling
The components use shadcn-style design tokens (--background, --input,
--ring, …). You have two options:
1. Already using shadcn/ui? Just import the stylesheet for the bundled utilities — the components automatically inherit your existing theme tokens:
import "dz-form-components/styles.css";2. Not using Tailwind/shadcn? The same import ships sensible light/dark
defaults out of the box (no Tailwind setup required). Add the dark class to
an ancestor element to switch to dark mode.
The shipped CSS contains only the utilities these components use and does not include a global reset, so it won't clobber your app's base styles.
Usage
Wilaya + Commune (dependent selects)
import { useState } from "react";
import { WilayaSelect, CommuneSelect } from "dz-form-components";
import type { WilayaValue, CommuneValue } from "dz-form-components";
import "dz-form-components/styles.css";
export function AddressForm() {
const [wilaya, setWilaya] = useState<WilayaValue | null>(null);
const [commune, setCommune] = useState<CommuneValue | null>(null);
return (
<div className="space-y-3">
<WilayaSelect lang="fr" value={wilaya?.code} onChange={setWilaya} />
<CommuneSelect
lang="fr"
wilayaCode={wilaya?.code}
value={commune?.name.fr}
onChange={setCommune}
showPostCode
/>
{commune && <p>Postal code: {commune.postCode ?? "—"}</p>}
</div>
);
}CommuneSelect is disabled until a wilaya is chosen and resets itself
whenever the parent wilaya changes.
Algerian phone
import { AlgerianPhoneInput } from "dz-form-components";
<AlgerianPhoneInput
onChange={({ nsn, e164, validation }) => {
// nsn: "661234567", e164: "+213661234567", validation.valid: true
}}
/>;The user types only the national number; the +213 prefix is fixed. Mobile
(5x/6x/7x) and landline (2x/3x/4x) formats are both accepted.
RC & NIF
import { RCInput, NIFInput } from "dz-form-components";
<RCInput
onChange={({ value, normalized, validation }) => {
// normalized: "16/16-0123456 B" when valid
}}
/>
<NIFInput
onChange={({ value, validation }) => {
// value is digits-only, capped at 15
}}
/>Inputs validate on blur by default (set validateOnBlur={false} for live
validation) and expose invalid for controlled error styling.
Languages & RTL
Every component takes a lang prop ("ar" | "fr" | "en", default "fr").
Passing "ar" switches labels to Arabic and sets dir="rtl" on the control.
<WilayaSelect lang="ar" /> // عرض بالعربية مع اتجاه من اليمين لليسارData & validation helpers
The same data and rules that power the components are exported for your own use:
import {
getWilayas, // () => Wilaya[] (all 58)
getWilaya, // (code) => Wilaya | undefined
getCommunes, // (wilayaCode) => Commune[]
validateRC,
validateNIF,
validateNIS,
validatePhone,
formatPhoneNational,
localized, // (name, lang) => string
} from "dz-form-components";
getWilaya("16")?.name.en; // "Algiers"
getCommunes("16").length; // 57
validatePhone("0661234567").valid; // true
formatPhoneNational("+213661234567"); // "0661 23 45 67"Need a hosted API or pure validators instead? See
dz-wilaya-apianddz-id-validator.
Component reference
WilayaSelect
| Prop | Type | Default | Notes |
| ------------- | ---------------------------------- | --------- | ------------------------------------ |
| value | string | — | Controlled wilaya code ("16"). |
| onChange | (v: WilayaValue \| null) => void | — | Fires with the full wilaya object. |
| lang | "ar" \| "fr" \| "en" | "fr" | Label/option language + direction. |
| showCode | boolean | true | Prefix options with the wilaya code. |
| placeholder | string | localized | — |
| disabled | boolean | false | — |
CommuneSelect
| Prop | Type | Default | Notes |
| --------------------- | ----------------------------------- | --------- | ------------------------------------ |
| wilayaCode | string | — | Parent wilaya; required to populate. |
| value | string | — | Controlled commune (French name). |
| onChange | (v: CommuneValue \| null) => void | — | Fires with the full commune object. |
| lang | "ar" \| "fr" \| "en" | "fr" | — |
| showPostCode | boolean | false | Append postal code to options. |
| disabledPlaceholder | string | localized | Shown before a wilaya is picked. |
AlgerianPhoneInput
| Prop | Type | Default | Notes |
| ---------------- | ------------------------------------- | ------- | ----------------------------------- |
| value | string | — | Controlled NSN (digits, no +213). |
| onChange | ({ nsn, e164, validation }) => void | — | e164 is null while invalid. |
| validateOnBlur | boolean | true | Show errors only after blur. |
| invalid | boolean | — | Force error styling. |
RCInput / NIFInput
| Prop | Type | Default | Notes |
| ---------------- | --------------------------------------------- | ------- | ---------------------------- |
| value | string | — | Controlled value. |
| onChange | ({ value, normalized, validation }) => void | — | normalized set when valid. |
| validateOnBlur | boolean | true | — |
| invalid | boolean | — | Force error styling. |
All components also forward native input/select props and className.
Development
npm install
npm run build:data # regenerate src/data/wilayas.ts from dz-wilaya-api
npm run typecheck
npm test
npm run build # tsup (JS+types) + Tailwind (styles.css)The bundled dataset is generated from
dz-wilaya-api. Place that repo
next to this one and run npm run build:data to refresh it.
License
MIT © HassanMak29
Part of the Algerian developer ecosystem:
dz-id-validator ·
dz-wilaya-api ·
dz-form-components
