OTP Input is a fully client-side Reactive Web Block for OutSystems that provides a professional, mobile-friendly One Time Password (OTP) or PIN input experience. It renders individual input boxes for each digit, handles auto-focus movement between boxes, supports paste, backspace navigation, error states, and fires an OutSystems event when the OTP is complete.
No external libraries required built entirely with vanilla JavaScript and OutSystems Reactive Web.
Ctrl+Q
OTPInputLib
OTPInput
OTPConfig
GetOTPValue
ClearOTP
SetOTPError
Before placing the block, add these Local Variables on your screen:
OTPWidgetId
""
EnteredOTP
In your screen's On Initialize event add an Assign node:
OTPConfig.Length = 6 OTPConfig.AutoSubmit = True OTPConfig.ShowSubmitButton = False OTPConfig.InputSize = 48 OTPConfig.SpacingBetween = 8 OTPConfig.FontSize = 20 OTPConfig.BorderColor = "#CCCCCC" OTPConfig.ActiveBorderColor = "#0057D9" OTPConfig.FilledBorderColor = "#00A86B" OTPConfig.ErrorBorderColor = "#E8173A" OTPConfig.Placeholder = "·" OTPConfig.MaskInput = False OTPConfig.IsError = False
Config
Wire SendContainerIDOnReady:
SendContainerIDOnReady
OTPReady
OTPWidgetId = WidgetId
Wire OnOTPComplete:
OnOTPComplete
OTPCompleted
EnteredOTP = OTPValue
Ctrl+P
WidgetId
OTPValue
Length
6
AutoSubmit
True
ShowSubmitButton
False
Placeholder
"·"
InputSize
48
SpacingBetween
8
BorderColor
"#CCCCCC"
ActiveBorderColor
"#0057D9"
FilledBorderColor
"#00A86B"
ErrorBorderColor
"#E8173A"
FontSize
20
IsError
MaskInput
Returns the current OTP value from all boxes.
Clears all boxes and focuses the first box.
Sets all boxes to error state (red border + shake animation).
DefaultBorderColor
OTPInput Block Config: Length = 6 AutoSubmit = True ShowSubmitButton = False InputSize = 48 BorderColor = "#CCCCCC" ActiveBorderColor = "#0057D9" FilledBorderColor = "#00A86B" ErrorBorderColor = "#E8173A" Events: SendContainerIDOnReady → OTPReady └── OTPWidgetId = WidgetId OnOTPComplete → OTPCompleted └── EnteredOTP = OTPValue └── Call VerifyOTP server action
OTPInput Block Config: Length = 4 AutoSubmit = False ShowSubmitButton = True MaskInput = True ← Hides digits like password InputSize = 52 FontSize = 24
User fills 4 boxes → clicks Verify OTP button → OnOTPComplete fires.
OTPCompleted Client Action ├── Input: OTPValue (Text) │ ├── Assign: EnteredOTP = OTPValue │ ├── Server Action: VerifyOTP │ ├── Input: OTPCode = EnteredOTP │ └── Output: IsValid (Boolean) │ └── If IsValid = True │ └── Navigate to Success Screen │ └── If IsValid = False └── SetOTPError ├── WidgetId = OTPWidgetId ├── IsError = True └── ErrorBorderColor = "#E8173A"
Resend OTP Button → OnClick ├── Server Action: ResendOTP │ └── ClearOTP ├── WidgetId = OTPWidgetId └── BorderColor = "#CCCCCC"
To clear the error state when user starts entering again:
OTPCompleted Client Action └── SetOTPError ├── WidgetId = OTPWidgetId ├── IsError = False └── DefaultBorderColor = "#CCCCCC"
OTPInput Block Config: Length = 6 InputSize = 56 FontSize = 24 SpacingBetween = 12 BorderColor = "#E0E0E0" ActiveBorderColor = "#6200EE" ← Purple active FilledBorderColor = "#03DAC6" ← Teal filled ErrorBorderColor = "#B00020" ← Dark red error Placeholder = "_"
Cause: OutSystems container default is display: block
display: block
Fix:
OTPContainer
display: flex; flex-direction: row; gap: 8px; justify-content: center; align-items: center;
Cause: Hidden trigger button not found in DOM
OTPTriggerBtn
opacity: 0; position: absolute; width: 0; height: 0; overflow: hidden; pointer-events: none;
display: none
Cause: SendContainerIDOnReady event not wired correctly
Cause: OTPWidgetId is empty when action is called
console.log("OTPWidgetId:", $parameters.WidgetId);
b1-OTPContainer
Cause: Some mobile browsers handle clipboard differently
Fix:This is a browser limitation on some mobile devices. Workaround — user can long press → paste on the first box and it will fill all boxes automatically.
Cause: JS keydown prevention not working on some mobile keyboards
Fix:Mobile keyboards fire events differently. The JS already handles this via:
var val = this.value.replace(/[^0-9]/g, "");
This strips non-numeric characters even if they get through the keydown check.
Cause: Structure attributes come as empty/zero when not set — not as default values
Fix:Always initialize OTPConfig in screen's On Initialize event:
OTPConfig.Length = 6 OTPConfig.BorderColor = "#CCCCCC" ... (all attributes)
Never rely on structure default values alone.
Cause: Both blocks share similar IDs
Fix:Currently the component is designed for one OTP block per screen. For multiple instances, contact the author for the multi-instance version.
MaskInput = True
ErrorLevel = "H"
IsError = True
inputmode="numeric"
AutoSubmit = True
ShowSubmitButton = False
AutoSubmit = False
ShowSubmitButton = True
Documentation Version: 1.0.0Component Version: 1.0.0Last Updated: 2026Author: — (your name here)