Skip to main content Skip to list of patterns
Service phase: Beta

This is a new service – give us your feedback to help improve it.

Patterns

Recover from validation errors

Allow users to understand issues with the information they provide and help them to fix it.

Contents

Check the information the user gives you to make sure it’s valid. If there’s a validation error, tell the user what’s gone wrong and how to fix it. Turn off HTML5 validation.

Follow GOV.UK advice on recovering from validation errors.

Use the error summary component to summarise the issues and provide links directly to the problem fields.

Open this example in new tab

HTML

<div class="tna-container">
  <div class="tna-column tna-column--full">
    <form novalidate>
      <h1 class="tna-heading-xl">Create a record</h1>
      <div class="tna-error-summary" data-module="tna-error-summary" data-disable-autofocus="true">
        <div role="alert">
          <h2 class="tna-error-summary__heading tna-heading-m">
            There is a problem
          </h2>
          <ul class="tna-error-summary__list">
            <li class="tna-error-summary__item">
              <a href="#record_file" class="tna-error-summary__link">Upload a file</a>
            </li>
            <li class="tna-error-summary__item">
              <a href="#record_name" class="tna-error-summary__link">The record name cannot contain emojis</a>
            </li>
            <li class="tna-error-summary__item">
              <a href="#record_categories-architectural" class="tna-error-summary__link">Select at least one category</a>
            </li>
            <li class="tna-error-summary__item">
              <a href="#record_date-day" class="tna-error-summary__link">The date of the record cannot be in the future</a>
            </li>
            <li class="tna-error-summary__item">
              <a href="#record_type-physical" class="tna-error-summary__link">Select the type of record</a>
            </li>
            <li class="tna-error-summary__item">
              <a href="#record_description" class="tna-error-summary__link">The record description cannot be blank</a>
            </li>
          </ul>
        </div>
      </div>
      <div class="tna-form-item tna-form-item--error">
        <div class="tna-form-item__header">
          <label class="tna-form-item__label tna-heading-m" for="record_file">
            Upload the record
          </label>
        </div>
        <p id="record_file-error" class="tna-form-item__error">
          <span class="tna-!--visually-hidden">Error:</span> Upload a file
        </p>
        <div class="tna-form-item__body">
          <input id="record_file" class="tna-file-input" name="record_file" type="file" aria-describedby=" record_file-error">
        </div>
      </div>
      <div class="tna-form-item tna-form-item--error">
        <div class="tna-form-item__header">
          <label class="tna-form-item__label tna-heading-m" for="record_name">
            Enter the name of the record
          </label>
        </div>
        <p id="record_name-error" class="tna-form-item__error">
          <span class="tna-!--visually-hidden">Error:</span> The record name cannot contain emojis
        </p>
        <div class="tna-form-item__body">
          <input id="record_name" class="tna-text-input" name="record_name" value="😎" aria-describedby=" record_name-error" type="text" spellcheck="false" autocapitalize="off" autocorrect="off">
        </div>
      </div>
      <div class="tna-fieldset tna-fieldset--small-hint tna-fieldset--error">
        <fieldset class="tna-fieldset__inner" aria-describedby="record_categories-error">
          <legend class="tna-fieldset__legend">
            <span class="tna-fieldset__heading tna-heading-m">
              Select the record categories
            </span>
          </legend>
          <p id="record_categories-error" class="tna-fieldset__error">
            <span class="tna-!--visually-hidden">Error:</span> Select at least one category
          </p>
          <div class="tna-fieldset__body">
            <div class="tna-checkboxes" id="record_categories">
              <div class="tna-checkboxes__item">
                <input type="checkbox" id="record_categories-architectural" value="architectural" name="categories">
                <label for="record_categories-architectural" class="tna-checkboxes__item-label">
                  Architectural
                </label>
              </div>
              <div class="tna-checkboxes__item">
                <input type="checkbox" id="record_categories-banking" value="banking" name="categories">
                <label for="record_categories-banking" class="tna-checkboxes__item-label">
                  Banking
                </label>
              </div>
              <div class="tna-checkboxes__item">
                <input type="checkbox" id="record_categories-charities" value="charities" name="categories">
                <label for="record_categories-charities" class="tna-checkboxes__item-label">
                  Charities
                </label>
              </div>
            </div>
          </div>
        </fieldset>
      </div>
      <div class="tna-fieldset tna-fieldset--small-hint tna-fieldset--error">
        <fieldset class="tna-fieldset__inner" aria-describedby="record_date-error">
          <legend class="tna-fieldset__legend">
            <span class="tna-fieldset__heading tna-heading-m">
              Enter the start date of the record
            </span>
          </legend>
          <p id="record_date-error" class="tna-fieldset__error">
            <span class="tna-!--visually-hidden">Error:</span> The date of the record cannot be in the future
          </p>
          <div class="tna-fieldset__body">
            <div class="tna-date-input" id="record_date">
              <div class="tna-date-input__item tna-date-input__item--day">
                <label for="record_date-day" class="tna-date-input__item-label">
                  Day
                </label>
                <input type="text" id="record_date-day" value="24" name="date-day" class="tna-date-input__item-input" inputmode="numeric">
              </div>
              <div class="tna-date-input__item tna-date-input__item--month">
                <label for="record_date-month" class="tna-date-input__item-label">
                  Month
                </label>
                <input type="text" id="record_date-month" value="09" name="date-month" class="tna-date-input__item-input" inputmode="numeric">
              </div>
              <div class="tna-date-input__item tna-date-input__item--year">
                <label for="record_date-year" class="tna-date-input__item-label">
                  Year
                </label>
                <input type="text" id="record_date-year" value="2086" name="date-year" class="tna-date-input__item-input tna-date-input__item-input--wider" inputmode="numeric">
              </div>
            </div>
          </div>
        </fieldset>
      </div>
      <div class="tna-fieldset tna-fieldset--small-hint tna-fieldset--error">
        <fieldset class="tna-fieldset__inner" aria-describedby="record_type-error">
          <legend class="tna-fieldset__legend">
            <span class="tna-fieldset__heading tna-heading-m">
              Select the type of record
            </span>
          </legend>
          <p id="record_type-error" class="tna-fieldset__error">
            <span class="tna-!--visually-hidden">Error:</span> Select the type of record
          </p>
          <div class="tna-fieldset__body">
            <div class="tna-radios" id="record_type">
              <div class="tna-radios__item">
                <input type="radio" id="record_type-physical" value="physical" name="type">
                <label for="record_type-physical" class="tna-radios__item-label">
                  Physical
                </label>
              </div>
              <div class="tna-radios__item">
                <input type="radio" id="record_type-digital" value="digital" name="type">
                <label for="record_type-digital" class="tna-radios__item-label">
                  Digital
                </label>
              </div>
            </div>
          </div>
        </fieldset>
      </div>
      <div class="tna-form-item tna-form-item--error">
        <div class="tna-form-item__header">
          <label class="tna-form-item__label tna-heading-m" for="record_description">
            Enter the record description
          </label>
        </div>
        <p id="record_description-error" class="tna-form-item__error">
          <span class="tna-!--visually-hidden">Error:</span> The record description cannot be blank
        </p>
        <div class="tna-form-item__body">
          <textarea id="record_description" class="tna-textarea" name="description" spellcheck="false" autocapitalize="off" autocorrect="off" rows="5" aria-describedby=" record_description-error"></textarea>
        </div>
      </div>
      <div class="tna-button-group">
        <button class="tna-button" type="submit">
          Create record
        </button>
        <button class="tna-button tna-button--plain" type="reset">
          Clear
        </button>
      </div>
    </form>
  </div>
</div>

Nunjucks

{% from "nationalarchives/components/button/macro.njk" import tnaButton %}
{% from "nationalarchives/components/checkboxes/macro.njk" import tnaCheckboxes %}
{% from "nationalarchives/components/date-input/macro.njk" import tnaDateInput %}
{% from "nationalarchives/components/error-summary/macro.njk" import tnaErrorSummary %}
{% from "nationalarchives/components/file-input/macro.njk" import tnaFileInput %}
{% from "nationalarchives/components/radios/macro.njk" import tnaRadios %}
{% from "nationalarchives/components/textarea/macro.njk" import tnaTextarea %}
{% from "nationalarchives/components/text-input/macro.njk" import tnaTextInput %}

<div class="tna-container">
  <div class="tna-column tna-column--full">
    <form novalidate>
      <h1 class="tna-heading-xl">Create a record</h1>
      {{ tnaErrorSummary({
        title: "There is a problem",
        items: [
          {
            text: "Upload a file",
            href: "#record_file"
          },
          {
            text: "The record name cannot contain emojis",
            href: "#record_name"
          },
          {
            text: "Select at least one category",
            href: "#record_categories-architectural"
          },
          {
            text: "The date of the record cannot be in the future",
            href: "#record_date-day"
          },
          {
            text: "Select the type of record",
            href: "#record_type-physical"
          },
          {
            text: "The record description cannot be blank",
            href: "#record_description"
          }
        ],
        disableAutoFocus: true
      }) }}
      {{ tnaFileInput({
        label: "Upload the record",
        headingSize: "m",
        id: "record_file",
        name: "record_file",
        error: {
          text: "Upload a file"
        }
      }) }}
      {{ tnaTextInput({
        label: "Enter the name of the record",
        headingSize: "m",
        id: "record_name",
        name: "record_name",
        value: "😎",
        error: {
          text: "The record name cannot contain emojis"
        }
      }) }}
      {{ tnaCheckboxes({
        label: "Select the record categories",
        headingSize: "m",
        id: "record_categories",
        name: "categories",
        items: [
          {
            text: "Architectural",
            value: "architectural"
          },
          {
            text: "Banking",
            value: "banking"
          },
          {
            text: "Charities",
            value: "charities"
          }
        ],
        error: {
          text: "Select at least one category"
        }
      }) }}
      {{ tnaDateInput({
        label: "Enter the start date of the record",
        headingSize: "m",
        id: "record_date",
        name: "date",
        value: {
          day: "24",
          month: "09",
          year: "2086"
        },
        error: {
          text: "The date of the record cannot be in the future"
        }
      }) }}
      {{ tnaRadios({
        label: "Select the type of record",
        headingSize: "m",
        id: "record_type",
        name: "type",
        items: [
          {
            text: "Physical",
            value: "physical"
          },
          {
            text: "Digital",
            value: "digital"
          }
        ],
        error: {
          text: "Select the type of record"
        }
      }) }}
      {{ tnaTextarea({
        label: "Enter the record description",
        headingSize: "m",
        id: "record_description",
        name: "description",
        error: {
          text: "The record description cannot be blank"
        }
      }) }}
      <div class="tna-button-group">
        {{ tnaButton({
          text: "Create record",
          buttonElement: true,
          buttonType: "submit"
        }) }}
        {{ tnaButton({
          text: "Clear",
          buttonElement: true,
          buttonType: "reset",
          plain: true
        }) }}
      </div>
    </form>
  </div>
</div>

Jinja

Important information

This is an experimental feature showing a Jinja2 macro based on the Nunjucks example. Check the converted code for accuracy.
{% from "components/button/macro.html" import tnaButton %}
{% from "components/checkboxes/macro.html" import tnaCheckboxes %}
{% from "components/date-input/macro.html" import tnaDateInput %}
{% from "components/error-summary/macro.html" import tnaErrorSummary %}
{% from "components/file-input/macro.html" import tnaFileInput %}
{% from "components/radios/macro.html" import tnaRadios %}
{% from "components/textarea/macro.html" import tnaTextarea %}
{% from "components/text-input/macro.html" import tnaTextInput %}

<div class="tna-container">
  <div class="tna-column tna-column--full">
    <form novalidate>
      <h1 class="tna-heading-xl">Create a record</h1>
      {{ tnaErrorSummary({
        "title": "There is a problem",
        "items": [
          {
            "text": "Upload a file",
            "href": "#record_file"
          },
          {
            "text": "The record name cannot contain emojis",
            "href": "#record_name"
          },
          {
            "text": "Select at least one category",
            "href": "#record_categories-architectural"
          },
          {
            "text": "The date of the record cannot be in the future",
            "href": "#record_date-day"
          },
          {
            "text": "Select the type of record",
            "href": "#record_type-physical"
          },
          {
            "text": "The record description cannot be blank",
            "href": "#record_description"
          }
        ],
        "disableAutoFocus": True
      }) }}
      {{ tnaFileInput({
        "label": "Upload the record",
        "headingSize": "m",
        "id": "record_file",
        "name": "record_file",
        "error": {
          "text": "Upload a file"
        }
      }) }}
      {{ tnaTextInput({
        "label": "Enter the name of the record",
        "headingSize": "m",
        "id": "record_name",
        "name": "record_name",
        "value": "😎",
        "error": {
          "text": "The record name cannot contain emojis"
        }
      }) }}
      {{ tnaCheckboxes({
        "label": "Select the record categories",
        "headingSize": "m",
        "id": "record_categories",
        "name": "categories",
        "items": [
          {
            "text": "Architectural",
            "value": "architectural"
          },
          {
            "text": "Banking",
            "value": "banking"
          },
          {
            "text": "Charities",
            "value": "charities"
          }
        ],
        "error": {
          "text": "Select at least one category"
        }
      }) }}
      {{ tnaDateInput({
        "label": "Enter the start date of the record",
        "headingSize": "m",
        "id": "record_date",
        "name": "date",
        "value": {
          "day": "24",
          "month": "09",
          "year": "2086"
        },
        "error": {
          "text": "The date of the record cannot be in the future"
        }
      }) }}
      {{ tnaRadios({
        "label": "Select the type of record",
        "headingSize": "m",
        "id": "record_type",
        "name": "type",
        "items": [
          {
            "text": "Physical",
            "value": "physical"
          },
          {
            "text": "Digital",
            "value": "digital"
          }
        ],
        "error": {
          "text": "Select the type of record"
        }
      }) }}
      {{ tnaTextarea({
        "label": "Enter the record description",
        "headingSize": "m",
        "id": "record_description",
        "name": "description",
        "error": {
          "text": "The record description cannot be blank"
        }
      }) }}
      <div class="tna-button-group">
        {{ tnaButton({
          "text": "Create record",
          "buttonElement": true,
          "buttonType": "submit"
        }) }}
        {{ tnaButton({
          "text": "Clear",
          "buttonElement": true,
          "buttonType": "reset",
          "plain": true
        }) }}
      </div>
    </form>
  </div>
</div>

Something not right?

We are working to improve this service and appreciate your input.