Skip to content

Validation

How it works

Handling server-side validation errors in Inertia works differently than a classic XHR-driven form that requires you to catch the validation errors from 422 responses and manually update the form's error state - because Inertia never receives 422 responses. Instead, Inertia operates much more like a standard full page form submission. Here's how:

First, you submit your form using Inertia. If there are server-side validation errors, you don't return those errors as a 422 JSON response. Instead, you redirect (server-side) the user back to the form page they were previously on, flashing the validation errors in the session.

ruby
class UsersController < ApplicationController
  def create
    user = User.new(user_params)

    if user.save
      redirect_to users_url
    else
      redirect_to new_user_url, inertia: { errors: user.errors }
    end
  end

  private

  def user_params
    params.require(:user).permit(:name, :email)
  end
end

Next, any time these validation errors are present in the session, they automatically get shared with Inertia, making them available client-side as page props which you can display in your form. Since props are reactive, they are automatically shown when the form submission completes.

Finally, since Inertia apps never generate 422 responses, Inertia needs another way to determine if a response includes validation errors. To do this, Inertia checks the page.props.errors object for the existence of any errors. In the event that errors are present, the request's onError() callback will be called instead of the onSuccess() callback.

Sharing errors

In order for your server-side validation errors to be available client-side, your server-side framework must share them via the errors prop. Inertia's Rails adapter does this automatically.

Displaying errors

Since validation errors are made available client-side as page component props, you can conditionally display them based on their existence. Remember, when using Rails server adapter, the errors prop will automatically be available to your page.

vue
<script setup>
import { reactive } from 'vue'
import { router } from '@inertiajs/vue3'

defineProps({ errors: Object })

const form = reactive({
  first_name: null,
  last_name: null,
  email: null,
})

function submit() {
  router.post('/users', form)
}
</script>

<template>
  <form @submit.prevent="submit">
    <label for="first_name">First name:</label>
    <input id="first_name" v-model="form.first_name" />
    <div v-if="errors.first_name">{{ errors.first_name }}</div>
    <label for="last_name">Last name:</label>
    <input id="last_name" v-model="form.last_name" />
    <div v-if="errors.last_name">{{ errors.last_name }}</div>
    <label for="email">Email:</label>
    <input id="email" v-model="form.email" />
    <div v-if="errors.email">{{ errors.email }}</div>
    <button type="submit">Submit</button>
  </form>
</template>

NOTE

When using the Vue adapters, you may also access the errors via the $page.props.errors object.

Repopulating input

While handling errors in Inertia is similar to full page form submissions, Inertia offers even more benefits. In fact, you don't even need to manually repopulate old form input data.

When validation errors occur, the user is typically redirected back to the form page they were previously on. And, by default, Inertia automatically preserves the component state for post, put, patch, and delete requests. Therefore, all the old form input data remains exactly as it was when the user submitted the form.

So, the only work remaining is to display any validation errors using the errors prop.

Error bags

NOTE

Error bags are a Laravel-specific feature that relies on Laravel's ViewErrorBag system. In Rails, this feature is typically unnecessary because:

  • Forms submit to separate controller actions, so only one set of errors is returned per request
  • The form helper automatically scopes validation errors to each form instance

If you have multiple forms on a page, use the useForm() helper and each form will maintain its own isolated error state.