Skip to content

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.

go
FormInput("name").
    Required(). // Generally accessible rule
    Ascii() // Attached to FormInput

Rule Callback

All form elements also contain a Rules callback, that allow you to directly call different rules:

go
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:

go
data := map[string][]string{
    "Name": []string{"Joe"}
}

Since "Joe" is less than 5 characters, this will pass validation.

go
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.

go
FormInput("Age").
    Integer()

Options Example

OptionsUnordered applies the In rule automatically to ensure the only accepted values server-side are either engineer or lawyer.

go
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.

go
// 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:

  1. The length of the value array for this field is > 0
  2. All values are NOT empty strings
go
FormInput("name").Required()

Filled

Ensures that:

  1. All values are NOT empty strings
go
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.

go
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).

go
// 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:

go
FormInput("Name").
    Prohibited()

Prohibits

Mutual exclusion helper: if this field is present (non-empty), the “other” field must be empty.

go
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

go
// Joe -> Valid
// Harry Smith -> Invalid

FormInput("Name").
    MaxLength(5)

Min Length

Ensures all values have lengths above or at the min length N

go
// Joe -> Invalid
// Harry Smith -> Valid

FormInput("Name").
    MinLength(5)

Exact Length

Ensures all values have lengths N

go
// Joe | Harry -> Invalid
// Zack -> Valid

FormInput("Name").
    ExactLength(4)

Length Between

Ensures all values are between N & M inclusive.

go
// 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

go
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).

go
// 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.

go
// 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

go
FormSelect("tags").
    ArrayDistinct()

Array Count

The length of the value array must equal N exactly

go
// 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

go
FormSelect("tags").
    ArrayMinCount(3)

Max Count

The length of the array must be less than or equal to N

go
FormSelect("tags").
    ArrayMaxCount(5)

Array Count Between

The length of the array must be between N & M inclusive.

go
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).

go
FormSwitch("taxed").
    Accepted()

Accepted If

Applies Accepted only when another field equals a specific value.

go
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).

go
FormSwitch("Taxed").
    Boolean()

Declined

Every non-empty value must be one of: no, off, 0, false (case-insensitive).

go
FormSwitch("Taxed").
    Declined()

Declined If

Applies Declined only when another field equals a specific value.

go
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.

go
// 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

go
// 2025-03-01 - Valid

FormTime("Birthday").
    Date()

Time

Values must be parsable as a time with format 15:04:05

go
// 03:12:34 - Valid

FormTime("Birthday").
    Time()

Time After

Parsed date must be strictly after the provided time.

go
FormTime("Appointment").
    TimeAfter(time.Now())

After or Equal

Time must be after or equal to the provided time.

go
FormTime("Appointment").
    TimeAfterOrEqual(time.Now())

Time Before

Time must be before the provided time.

go
FormTime("Appointment").
    TimeBefore(time.Now())

Before or Equal

Time must be before or equal to the provided time.

go
FormTime("Appointment").
    TimeBeforeOrEqual(time.Now())

Time Equals

Time must exactly match the provided time.

go
FormTime("Appointment").
    TimeEquals(time.Now())

Hour Increment

The provided time must have hours that are a multiple of the given increment.

go
// 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.

go
// 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

go
FormInput("Age").
    Numeric()

Decimals

Ensures each value has a N count of decimals

go
// 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

go
FormInput("Cost").
    Integer()

Number Between

Parsed number within (min, max).

go
FormInput("Cost").
    NumberBetween(12.00, 99.99)

Digits

Ensures each value has N count of digits.

go
// 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.

go
FormInput("Age").
    EqualsInteger(25)

Equals

Equals float parses each value into a float64 and compares it the the target.

go
FormInput("Time").
    Equals(12.03021)

// With custom epsilon
FormInput("Time").
    Equals(12.030231, 1e-7)

Greater Than

Parsed float64 number > threshold.

go
FormInput("Cost")
    GreaterThan(12.01)

Greater Than Or Equal

Parsed float64 number >= threshold.

go
FormInput("Cost").
    GreaterThanOrEqual(12.01)

// With custom epsilon 
FormInput("Cost").
    GreaterThanOrEqual(12.01, 1e-7)

Less Than

Parsed float64 number < threshold.

go
FormInput("Cost")
    LessThan(12.01)

Less Than Or Equal

Parsed float64 number <= threshold.

go
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.

go
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

go
.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.

Released under the MIT License.