React component
An account number input with formatting, validation and bank detection.
@kontosjekker/react ships an account input that formats as the user types, shows
the owning bank’s logo and name, and reports MOD-11 validity. It is unstyled
beyond layout — you pass class names, the way a shadcn/ui component works.
pnpm add @kontosjekker/react
Against the API
import { BankAccountInput, remoteSource } from "@kontosjekker/react"
import { useMemo, useState } from "react"
export function PayoutForm() {
const [account, setAccount] = useState("")
// Memoise: the source caches lookups per four digit prefix.
const source = useMemo(
() => remoteSource({ baseUrl: "https://api.kontosjekker.asamsig.com" }),
[],
)
return (
<BankAccountInput
value={account}
onChange={setAccount}
source={source}
classNames={{ input: "h-9 w-full rounded-md border px-3" }}
/>
)
}
Offline, with the list bundled
No network at all — the register is compiled into your bundle. Good for forms that must work offline, at the cost of shipping ~120 kB and going stale between releases.
import { banks } from "@kontosjekker/data"
import { BankAccountInput, localSource } from "@kontosjekker/react"
const source = localSource({ banks, logoBaseUrl: "/bank-logos" })
Copy the logo files you need from @kontosjekker/data/logos into your public
directory; logoBaseUrl is where you serve them from.
Just the logic
import { useBankAccount } from "@kontosjekker/react"
const { formatted, validation, bank, logoUrl, isLoadingBank } = useBankAccount({
value,
source,
})
validation stays null until 11 digits have been entered, so the user is not
told they are wrong while still typing. Pass validateWhileTyping to change
that.
Validation without React
@kontosjekker/core is dependency-free and runs anywhere:
import { validateBban, bbanToIban } from "@kontosjekker/core"
validateBban("8601 11 17947").valid // true
bbanToIban("86011117947") // "NO9386011117947"