Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11The Android half of a React Native biometric library is a thin native module. It exposes two or three methods to JavaScript, hands the real work to AndroidX BiometricPrompt in Kotlin, and translates every native callback into one stable result shape. “Lightweight” should come from what you leave out: no custom UI, no hidden state, no business logic. This guide covers that design, the Kotlin code, the fallback decisions, and the limits of what a successful prompt proves.
This is implementation guidance based on Android’s documentation and on two existing open-source libraries. It is not a report of hands-on device testing, and no size or speed figures are claimed.
Decide first: UI gate or cryptographic proof?
Everything else follows from this choice, so make it before writing code.
- Prompt-only gate. The app asks the system to verify the user locally and receives success or failure. This is fine for revealing a screen, confirming a sensitive in-app action, or unlocking locally cached data. It does not prove anything to your server.
- Key-backed operation. Android’s prompt has an authenticate overload that takes a
CryptoObject, so a keystore key can be usable only after authentication. A server login needs a deliberate design: a native keystore-backed key pair, a server-issued challenge, a signature produced after authentication, and server-side verification.
The SelfLender react-native-biometrics library takes this split seriously. It describes public/private keys held in native keystores, protected by biometrics, with signatures created after authentication. It also offers a separate simplePrompt for gating in-app actions, and its documentation cautions that a prompt-only result should not be used as server login authentication. Adopt the same wording in your own docs: “the device accepted local verification” is not “the backend authenticated this account.”
#1 Best Overall
- Document link: https://tinyurl(DOT)com/Fringerprint-Sensor
- Storage Capacity: 240 fingerprints
- This module can be controlled through the serial port, or using the computer's serial port
- The product consists of optical fingerprint sensor, high-speed DSP processor, high-performance fingerprint matching algorithm, ultra-large capacity FLASH chip and other hardware and software
- This fingerprint module has stable performance, complete functions, and has multiple functions such as fingerprint collection, fingerprint registration, fingerprint matching, and fingerprint search
The rest of this article builds the prompt-only gate, which is the smallest useful library. Key-backed signing is a separate feature with its own key-management, invalidation, and enrollment-change questions, and you should not imply a simple prompt provides it.
What Android gives you, and which API to use
Android’s framework BiometricPrompt is documented as “a class that manages a system-provided biometric dialog” and requires API 28 (Android 9). The AndroidX BiometricPrompt wraps it: per Android Developers, it uses the system prompt on Android 9 and later and a custom fingerprint dialog on earlier supported versions. For a library that must run on a range of React Native app configurations, the AndroidX class is the practical choice because the compatibility handling is not yours to maintain.
| Question | Framework android.hardware.biometrics.BiometricPrompt |
AndroidX androidx.biometric.BiometricPrompt |
|---|---|---|
| Minimum Android | API 28 | System prompt on API 28+, custom fingerprint dialog on earlier supported versions (Android Developers) |
| Dialog | System-provided | System-provided on API 28+; library-provided before |
| Maintenance burden for you | You handle version differences | Library handles them; you pin and test a dependency version |
Check the current AndroidX Biometric artifact version and its supported-OS notes when you implement; this article does not pin one.
One behavior matters for your contract: AndroidX states that “for security reasons, the prompt will be dismissed when the client application is no longer in the foreground.” Your module must treat that dismissal as a normal outcome, not an edge case.
Design the JavaScript contract
Keep the surface to three calls: check availability, authenticate, cancel. Return outcomes as data instead of throwing for expected situations such as a user pressing cancel. Reserve rejected promises for programmer errors (no foreground activity, bad options).
Rank #2
- Advanced ZW101 Fingerprint Recognition Module with low-power finger detection technology for high accuracy in fingerprint scanning and identification
- Features a capacitive semiconductor fingerprint sensor with a protective coating, RGB LED lights, and UART interface for reliable fingerprint reading
- Securely store up to 50 fingerprint features with ESD protection exceeding 15KV, ensuring top-notch security for applications like fingerprint door locks and safes
- Lightning-fast response time with feature extraction in under 0.06 seconds and a false acceptance rate (FAR) below 1/1000000 for seamless identity verification
- Perfect for a wide range of industries including finance, security, and management, offering a versatile solution for access control systems, POS terminals, and time attendance machines
// index.ts
import { NativeModules } from 'react-native';
export type Availability =
| 'available'
| 'none_enrolled'
| 'no_hardware'
| 'hardware_unavailable'
| 'security_update_required'
| 'unsupported';
export type AuthResult =
| { success: true }
| { success: false; reason:
| 'user_canceled' | 'negative_button' | 'lockout'
| 'lockout_permanent' | 'not_available' | 'timeout'
| 'interrupted' | 'superseded' | 'error'; message?: string };
export interface AuthOptions {
title: string;
subtitle?: string;
cancelLabel?: string; // required when device credential is NOT allowed
allowDeviceCredential?: boolean;
}
const Native = NativeModules.LightBiometrics;
export const getAvailability = (allowDeviceCredential = false): Promise<Availability> =>
Native.getAvailability(allowDeviceCredential);
export const authenticate = (o: AuthOptions): Promise<AuthResult> => Native.authenticate(o);
export const cancel = (): void => Native.cancel();
Write the Kotlin module
Dependencies
Add the AndroidX Biometric artifact to the library’s build.gradle (use the current stable version from Android Developers’ release notes):
dependencies {
implementation "androidx.biometric:biometric:<current-stable-version>"
}
The prompt needs a FragmentActivity. React Native’s default MainActivity extends ReactActivity, which is a AppCompatActivity and therefore qualifies, but check this for apps with custom activities. The Android reference lists the USE_BIOMETRIC permission for the relevant operations; declare it in the library manifest and confirm against current documentation for the API levels you target.
Availability check
private fun authenticators(allowCredential: Boolean) =
if (allowCredential) BIOMETRIC_STRONG or DEVICE_CREDENTIAL else BIOMETRIC_STRONG
@ReactMethod
fun getAvailability(allowCredential: Boolean, promise: Promise) {
val status = BiometricManager.from(reactContext)
.canAuthenticate(authenticators(allowCredential))
promise.resolve(when (status) {
BiometricManager.BIOMETRIC_SUCCESS -> "available"
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> "none_enrolled"
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> "no_hardware"
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> "hardware_unavailable"
BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> "security_update_required"
else -> "unsupported"
})
}
Treat availability as advisory. State can change between the check and the prompt (a user can remove their enrollment), so authenticate must still handle failure.
Authenticate with a single in-flight attempt
The most common bug in bridge code is a promise that never settles. Guard against it with one pending slot and a settle-once helper.
class LightBiometricsModule(private val ctx: ReactApplicationContext) :
ReactContextBaseJavaModule(ctx), LifecycleEventListener {
override fun getName() = "LightBiometrics"
private class Pending(val promise: Promise, val prompt: BiometricPrompt) {
private val done = AtomicBoolean(false)
fun settle(map: WritableMap) { if (done.compareAndSet(false, true)) promise.resolve(map) }
}
private val pending = AtomicReference<Pending?>(null)
init { ctx.addLifecycleEventListener(this) }
@ReactMethod
fun authenticate(opts: ReadableMap, promise: Promise) {
val activity = ctx.currentActivity as? FragmentActivity
?: return promise.reject("E_NO_ACTIVITY", "No foreground FragmentActivity")
val allowCred = opts.hasKey("allowDeviceCredential") && opts.getBoolean("allowDeviceCredential")
activity.runOnUiThread {
// Supersede any earlier attempt so its promise is settled, not leaked.
pending.getAndSet(null)?.let { old ->
old.settle(failure("superseded"))
old.prompt.cancelAuthentication()
}
val executor = ContextCompat.getMainExecutor(activity)
lateinit var current: Pending
val callback = object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(r: BiometricPrompt.AuthenticationResult) {
pending.compareAndSet(current, null)
current.settle(Arguments.createMap().apply { putBoolean("success", true) })
}
override fun onAuthenticationError(code: Int, msg: CharSequence) {
pending.compareAndSet(current, null)
current.settle(failure(mapError(code), msg.toString()))
}
// onAuthenticationFailed = one bad attempt; the prompt stays open. Do not settle.
}
val prompt = BiometricPrompt(activity, executor, callback)
current = Pending(promise, prompt)
pending.set(current)
val info = BiometricPrompt.PromptInfo.Builder()
.setTitle(opts.getString("title") ?: "Authenticate")
.apply { opts.getString("subtitle")?.let(::setSubtitle) }
.setAllowedAuthenticators(authenticators(allowCred))
.apply {
// A negative button is required without device credential, and not allowed with it.
if (!allowCred) setNegativeButtonText(opts.getString("cancelLabel") ?: "Cancel")
}
.build()
prompt.authenticate(info)
}
}
@ReactMethod
fun cancel() {
pending.get()?.let { p ->
ctx.currentActivity?.runOnUiThread { p.prompt.cancelAuthentication() }
}
}
override fun onHostPause() {
// The prompt is dismissed when the app leaves the foreground; make sure JS hears about it.
pending.getAndSet(null)?.settle(failure("interrupted"))
}
override fun onHostResume() {}
override fun onHostDestroy() { pending.getAndSet(null)?.settle(failure("interrupted")) }
}
Two notes on this sketch. First, onAuthenticationFailed (a non-matching fingerprint or face) is not terminal in the prompt, so it deliberately does nothing; resolving there would end an attempt the user is still making. Second, depending on the activity and prompt behavior, onHostPause can fire for reasons other than the prompt (for example, the system dialog itself on some devices). Verify on real devices that your pause handling does not settle a prompt that is still legitimately showing; if it does, rely on the error callback and settle only in onHostDestroy.
Rank #3
- Optical fingerprint sensor secure your project with biometrics. This fingerprint module can be used for fingerprint collection, fingerprint registration, fingerprint comparison and fingerprint search, it's easy to use, so its perfect for any project
- Fingerprint sensor module can work with any microcontroller which with serial port: such as compatible with arduino, 51, avr, stm32, pic, arm, msp430
- Package Includes:1 X Optical Fingerprint Reader Sensor, 2 X Cable. You can enroll new fingers directly - up to 240 finger prints can be stored
- Applications: Fingerprint door locks, safes, guns, financial and other security areas; Access control systems, industrial computers, POS machines, driving training, attendance and other areas of identity; fingerprint payment and other financial areas
- The fingerprint moudle documentation link cannot be displayed. If you need technical documentation, please click “Geekstory” to em-ail us
Map native errors to your stable reasons
private fun mapError(code: Int) = when (code) {
BiometricPrompt.ERROR_USER_CANCELED -> "user_canceled"
BiometricPrompt.ERROR_NEGATIVE_BUTTON -> "negative_button"
BiometricPrompt.ERROR_CANCELED -> "interrupted"
BiometricPrompt.ERROR_LOCKOUT -> "lockout"
BiometricPrompt.ERROR_LOCKOUT_PERMANENT -> "lockout_permanent"
BiometricPrompt.ERROR_TIMEOUT -> "timeout"
BiometricPrompt.ERROR_NO_BIOMETRICS,
BiometricPrompt.ERROR_NO_DEVICE_CREDENTIAL,
BiometricPrompt.ERROR_HW_NOT_PRESENT,
BiometricPrompt.ERROR_HW_UNAVAILABLE -> "not_available"
else -> "error"
}
private fun failure(reason: String, message: String? = null) =
Arguments.createMap().apply {
putBoolean("success", false)
putString("reason", reason)
message?.let { putString("message", it) }
}
Keep the human-readable message as a diagnostic only. Application logic should branch on reason, which your library controls, and not on platform strings that vary by OEM and locale.
Fallback policy: biometrics only, or device credential too
The library should not decide this silently. Expose it as allowDeviceCredential and document what each setting means.
Recommended Free Tools
- Biometric only: the prompt needs a cancel/negative button, and a user without enrolled biometrics gets
none_enrolledfrom the availability check. Your app must offer its own alternative path. - Biometric plus device PIN/pattern/password: the same system prompt offers the credential. With this option the negative button text must not be set, as the sketch above does.
Be careful about generalizing API-level limits. SelfLender documents that its own allowDeviceCredentials option is not supported on Android before API 30; that is a statement about that package, not a universal Android restriction. AndroidX also documents restrictions on some authenticator combinations on older API levels, particularly alongside a CryptoObject, so test your chosen combination on the oldest API level you claim to support instead of assuming it works everywhere.
Also decide what a device-credential success means for your feature. If a key is bound to strong biometrics only, a PIN entry will not unlock it; the prompt-only gate has no such distinction, which is one more reason to document the guarantee precisely.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Outcomes your app should handle explicitly
| Outcome | Typical cause | Suggested app behavior |
|---|---|---|
success |
Local verification accepted | Proceed with the local action only; do not treat as server login |
user_canceled / negative_button |
User dismissed the prompt or chose the alternative | Stay locked or show your alternative path; no error banner |
lockout |
Too many failed attempts, temporary | Offer device credential if allowed, or tell the user to wait |
lockout_permanent |
Biometrics disabled until stronger authentication | Route to device credential or account sign-in |
not_available |
No enrollment, hardware missing or busy | Hide the feature or direct the user to settings |
interrupted |
App backgrounded or system cancelled | Allow a retry on return; never auto-grant |
superseded |
A newer call replaced this one | Ignore; the newer call owns the UI |
timeout, error |
Platform timeout or other failure | Offer retry and a non-biometric path |
The rule is simple: only success: true unlocks anything. Every other branch is a distinct, handled non-success.
Rank #4
- Certified to Microsoft’s highest fingerprint security standards (ESS & SDCP) for robust, hardware-isolated authentication. Supports next-gen Windows features, including Copilot Recall and Windows Hello with ESS support.
- Windows Hello ready for fast, password free fingerprint login to Windows and Microsoft 365 accounts
- On device fingerprint storage keeps biometric data securely within the key. Supports privacy regulations (GDPR, BIPA, CCPA) through on device biometric processing; TAA compliant.
- Reliable wired USB fingerprint authentication with USB C and USB A compatibility for desktop PCs.
- Consistent, all condition 360° fingerprint recognition.
Compatibility work that is separate from the Kotlin
Writing correct Kotlin does not make a library compatible with every React Native setup. Treat these as three separate acceptance tracks:
- Old versus new architecture. A module written as above uses the legacy native-module style. Supporting the new architecture means defining a typed spec and codegen output and testing with it enabled. The
@sbaiahmed1/react-native-biometricsrepository claims support for both architectures, but that is a maintainer claim about that package, not evidence about yours. - Expo. Expo apps that use custom native code need a development build, and a config plugin if your library requires manifest or Gradle changes. The same repository documents Expo configuration; again, treat it as a checklist item for you to verify.
- Activity assumptions. Apps with a non-
FragmentActivityhost, multiple activities, or an embedded React Native view will break thecurrentActivity as? FragmentActivitycast. Fail with a clear rejection.
Build or adopt?
Two existing libraries are worth reading before you commit, and both are maintainer documentation rather than audited products. @sbaiahmed1/react-native-biometrics documents Kotlin on Android, availability checks, prompts, device credential fallback, key functions, Expo configuration, and old/new architecture support. SelfLender/react-native-biometrics documents keystore-managed keypairs, signing, and simplePrompt. Repository pages change, and current release numbers and maintenance activity are not established here, so check both before deciding.
Compare candidates, including your own, on the same axes:
- Prompt-only gate versus key-backed signing.
- Framework API versus AndroidX compatibility API.
- Biometric-only versus device-credential fallback.
- Legacy bridge versus new-architecture support.
- Dependency count and binary size, measured on the same build and device baseline.
- Defined behavior for cancellation, lockout, missing sensors, and backgrounding.
Be careful with “lightweight”
The existing library that markets itself as lightweight with minimal dependencies publishes no reproducible measurement in its documentation, and no benchmark figure was found for either library. If you call yours lightweight, define it: the AAR size delta in a release build, transitive dependency count, and any latency number with the device, API level, and build type stated. Without those, describe the design (“one module, one AndroidX dependency, no custom UI”) and let readers judge.
Quick Recap
Pre-release test matrix
- Lowest and highest API levels you claim, plus API 28/29 and 30+ if you offer device-credential fallback.
- Device with no enrollment, with fingerprint, and with face unlock.
- Cancel via back, via the negative button, and via
cancel()from JS. - Press Home while the prompt is open, then return.
- Rapid double call to
authenticate; confirm both promises settle. - Repeated failures until lockout.
- Old and new architecture builds, and an Expo development build.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minute




