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.
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
{% 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>