Validation
Iridium is a SSR UI framework, meaning validation is typically written once on the backend and validated on certain requests, like when saving your form to a model.
Directly Accessible
Some form elements have specific rules directly attached to them, which can be called directly.
FormInput("name").
Required(). // Generally accessible rule
Ascii() // Attached to FormInputRule Callback
All form elements also contain a Rules callback, that allow you to directly call different rules:
import "github.com/iridiumgo/iridium/core/rules"
FormInput("name").
Rules(
rules.Required(),
rules.Ascii(),
)Value Arrays
All field values are held behind the scenes as []string. For example, a form input set to the word "Hello" will have it value contained as []string{"Hello"}.
Rules iterate through these string arrays and apply their rules to each individual string value.
Example
Imagine the form submission data as:
data := map[string][]string{
"Name": []string{"Joe"}
}Since "Joe" is less than 5 characters, this will pass validation.
FormInput("Name").
MaxLength(5)If you have a multi-value input that expects many values (like a Select box), its rules apply to all options, and if any value fails, it fails validation for that field.
Implicit Rules
When customizing your form inputs, Iridium will often apply a server-side rule on your behalf. This is done to prevent mistakes when creating your form where you forget to apply a relevant rule for a particular configuration.
Integer Example
Integer not only sets the <input/> type to number, but automatically applies both the Numeric and Integer rule on your behalf.
FormInput("Age").
Integer()Options Example
OptionsUnordered applies the In rule automatically to ensure the only accepted values server-side are either engineer or lawyer.
FormSelect("Jobs").
OptionsUnordered(map[string]string{
"engineer": "Engineer",
"lawyer": "Lawyer"
})Which methods have implicit rules?
In our documentation, you'll find references to implicit rules, to know which methods apply relevant rules and which do not.
No Implicit Rules
If a field is not marked as implicit, it is your responsibility as the developer to write a custom rule to ensure it's server-side validated.
Disable Implicit Rules
If you wish to disable these implicit rules, you can apply NoImplicitRules to your form element.
Your rules and custom rules will still be applied, but all implicit rules will be skipped.
// Implicit rule `In` is no longer applied
// Meaning the server will not validate the selected keys for you anymore.
FormSelect("Jobs").
NoImplicitRules().
OptionsUnordered(map[string]string{
"engineer": "Engineer",
"lawyer": "Lawyer"
})General
General rules are rules which are shared by all form components and are directly assessible on them all.
Required
Ensures that:
- The length of the value array for this field is > 0
- All values are NOT empty strings
FormInput("name").Required()Filled
Ensures that:
- All values are NOT empty strings
FormInput("name").Filled()Required does not accept []string{}, but Filled does. It only ensures if there is an array element, it is not "".
Required If
Conditional Required when another field’s first value equals a specific string.
FormSwitch("over25")
// Only require a user's age if they are over 25.
FormInput("Age").
RequiredIf("over25", "false")Required With
Required if any of the listed fields are present (non-empty after trimming).
// A car's make and model are both optional
// but if you specifiy a `Make` you must specify a `Model`.
FormSwitch("Make")
FormInput("Model").
RequiredWith("Make")Prohibited
Field must either be a length of zero, or contain all empty strings:
FormInput("Name").
Prohibited()Prohibits
Mutual exclusion helper: if this field is present (non-empty), the “other” field must be empty.
FormInput("A")
// If B is set, A must equal ""
// If B is NOT set, A can equal anything
FormInput("B").
Prohibits("A")Max Length
Ensures all values have lengths below or at the max length N
// Joe -> Valid
// Harry Smith -> Invalid
FormInput("Name").
MaxLength(5)Min Length
Ensures all values have lengths above or at the min length N
// Joe -> Invalid
// Harry Smith -> Valid
FormInput("Name").
MinLength(5)Exact Length
Ensures all values have lengths N
// Joe | Harry -> Invalid
// Zack -> Valid
FormInput("Name").
ExactLength(4)Length Between
Ensures all values are between N & M inclusive.
// Joe | Harry -> Valid
// Mo | Alexandar -> Invalid
FormInput("Name").
LengthBetween(3,5)Bail
When validating, all rules are run sequentially, and then all errors are reported back.
If you'd prefer to early terminate if a single rule fails, you can use Bail
import "github.com/iridiumgo/iridium/core/rules"
FormInput("Name").
Rules(
rules.Bail(
rules.Required(),
rules.MaxLength(5), // Will not run if required failed.
),
)Bail is only accessible when using the Rules callback
Arrays
The following rules deal with the []string array directly, and are often used for multi-value fields like the FormSelect.
Array Contains
Passes if any submitted value equals the provided needle (exact string match).
// Selected options must contain "tag1" at least one time.
FormSelect("tags").
ArrayContains("tag1")Array Doesn’t Contain
Fails if any submitted value equals the provided needle.
// A user can not select tag2 as an option
FormSelect("tags").
ArrayContains("tag2")Distinct
All values after being trimmed, must be unique to the array
FormSelect("tags").
ArrayDistinct()Array Count
The length of the value array must equal N exactly
// User must select 3 tags
FormSelect("tags").
ArrayCount(3)TIP
It's worth applying Filled or Required to ensure any "" will fail validation. ArrayCount counts "" to the total array length.
Array Min Count
The length of the array must be greater than or equal to N
FormSelect("tags").
ArrayMinCount(3)Max Count
The length of the array must be less than or equal to N
FormSelect("tags").
ArrayMaxCount(5)Array Count Between
The length of the array must be between N & M inclusive.
FormSelect("tags").
ArrayCountBetween(3,5)Booleans
Rules which apply to validating boolean states. Commonly used with FormSwitch
Accepted
Every non-empty value must be one of: yes, on, 1, true (case-insensitive).
FormSwitch("taxed").
Accepted()Accepted If
Applies Accepted only when another field equals a specific value.
FormInput("Name")
// Taxed must be true if Name is "Joe".
FormSwitch("Taxed").
AcceptedIf("Name", []string{"Joe"})Boolean
Every non-empty value must be one of: true, false, 1, 0, on, off, yes, no (case-insensitive).
FormSwitch("Taxed").
Boolean()Declined
Every non-empty value must be one of: no, off, 0, false (case-insensitive).
FormSwitch("Taxed").
Declined()Declined If
Applies Declined only when another field equals a specific value.
FormInput("Name")
// Taxed must be false if Name is "Joe".
FormSwitch("Taxed").
AcceptedIf("Name", []string{"Joe"})Dates
Dates rules are intended to be used with the FormTime component.
Date Time
Values must be parsable as a date & time.
// 2024-05-01 - Invalid, no time
// 03:23:12 - Invalid, no date
// 2025-04-04T03:23:12.000 - Valid date and time string
FormTime("Birthday").
DateTime()Date
Values must be parsable as a date with format 2006-01-02
// 2025-03-01 - Valid
FormTime("Birthday").
Date()Time
Values must be parsable as a time with format 15:04:05
// 03:12:34 - Valid
FormTime("Birthday").
Time()Time After
Parsed date must be strictly after the provided time.
FormTime("Appointment").
TimeAfter(time.Now())After or Equal
Time must be after or equal to the provided time.
FormTime("Appointment").
TimeAfterOrEqual(time.Now())Time Before
Time must be before the provided time.
FormTime("Appointment").
TimeBefore(time.Now())Before or Equal
Time must be before or equal to the provided time.
FormTime("Appointment").
TimeBeforeOrEqual(time.Now())Time Equals
Time must exactly match the provided time.
FormTime("Appointment").
TimeEquals(time.Now())Hour Increment
The provided time must have hours that are a multiple of the given increment.
// 03:00:00 - Invalid
// 04:00:00 - Valid
FormTime("Appointment").
HourIncrement(2)Minute Increment
The provided time must have minutes that are a multiple of the given increment.
// 03:10:00 - Invalid
// 03:30:00 - Valid
FormTime("Appointment").
MinuteIncrement(15)Numbers
Applies rules related to numbers. Generally used with the FormInput component.
Number comparisons are done in float64 with a default epsilon of 1e-9 (except rules marked with the work Integer). The epsilon is overridable.
Numeric
Ensures each value is float parsable. I.E. passes strconv.ParseFloat
FormInput("Age").
Numeric()Decimals
Ensures each value has a N count of decimals
// 1, 1.1, 1.234 -> Invalid
// 1.12, 0.55, 0.01 -> Valid
FormInput("Cost").
Decimals(2)Integer
Ensures each value is int parsable. I.E. passes strconv.ParseInt
FormInput("Cost").
Integer()Number Between
Parsed number within (min, max).
FormInput("Cost").
NumberBetween(12.00, 99.99)Digits
Ensures each value has N count of digits.
// 2, 323, 0 -> Invalid
// 22, 03 -> Valid
FormInput("Age").
Digits(2)Equals Integer
Equals integer parses each value into a int64 and compares with the target.
FormInput("Age").
EqualsInteger(25)Equals
Equals float parses each value into a float64 and compares it the the target.
FormInput("Time").
Equals(12.03021)
// With custom epsilon
FormInput("Time").
Equals(12.030231, 1e-7)Greater Than
Parsed float64 number > threshold.
FormInput("Cost")
GreaterThan(12.01)Greater Than Or Equal
Parsed float64 number >= threshold.
FormInput("Cost").
GreaterThanOrEqual(12.01)
// With custom epsilon
FormInput("Cost").
GreaterThanOrEqual(12.01, 1e-7)Less Than
Parsed float64 number < threshold.
FormInput("Cost")
LessThan(12.01)Less Than Or Equal
Parsed float64 number <= threshold.
FormInput("Cost")
LessThanOrEqual(12.01)
// With custom epsilon
FormInput("Cost")
LessThanOrEqual(12.01, 1e-7)Step
Step validate the provided field is a multiple of a chosen increment N.
FormInput("Volume").
Step(5)
// With a custom epsilon
FormInput("Volumn").
Step(0.0001, 1e-6)Strings
Format/style checks skip empty values.
Alpha
Letters A–Z only.
Alpha Numeric
Letters and digits only.
Email
Basic email pattern check.
In
Every value must be in the allowed set (exact match).
Not In
No value may be in the forbidden set.
Matches
Must match the provided regex. Error message can be customized.
URL
Must start with http(s)😕/ and include a host.
UUID
Canonical UUID v1–v5.
Active URL
Basic “active-style” URL: http(s) scheme + host.
AlphaDash
Letters, hyphen, underscore.
Ascii
ASCII printable characters only.
Confirmed
Must equal the other field’s first value (e.g., password confirmation).
Different Field
Must differ from the other field’s first value (string compare).
Doesn’t Start With
May not start with any of the provided prefixes.
Doesn’t End With
May not end with any of the provided suffixes.
Ends With
Must end with at least one of the provided suffixes.
Hex Colour
#RGB, #RRGGBB, or #RRGGBBAA.
IP Address
Valid IPv4 or IPv6 textual address.
Json
Must be valid JSON (any JSON type).
Mac Address
Standard hex-byte forms (e.g., 01:23:45:67:89:ab).
Not Regex Rule
Must NOT match the provided regex.
Same Field
Must equal the other field’s first value (string compare).
Starts With Rule
Must start with at least one of the provided prefixes.
Uppercase
Must be all uppercase.
ULID
Valid 26-char Crockford Base32; first char not 0.
Driver
coming soon...
Files
coming soon...
Custom Rules
Custom rules allow you define your own rules on for your form element. As discussed above, all validation is done against []string which is golangs default way of parsing form data.
Your custom rule must return a boolean and string. Boolean indicates if the validation passed (true == pass), and the string is the message to display on a failure.
Example
.Rules(
rules.Custom[models.User](
func(val []string, ctx *context.FieldContext[models.User]) (bool, string) {
if model, err := ctx.GetModel(); err != nil && model.Name != "Joe Smith" {
return false, "Need to be Joe Smith"
}
return true, ""
},
),
)Client-side validation
Iridum supports basic client side validation based on HTML input rules.
You are also able to utilize our attributes system for client side validation with Alpine.js.
WARNING
Please remember when using client side validation to include the same rule(s) on the backend. It is trivial for an attacker to skip your client side validation.