Skip to main content

betaThis is a new capability. Help us improve it and give your feedback on Slack.

Page templates

Page templates are a configuration-based way of adding dynamic content to the form UI, such as displaying the answer to a question, or some data from your API. This feature is only used for presentation purposes.

Certain elements of your form, such as content blocks or page titles, allow for the use of LiquidJS. LiquidJS is a templating engine that runs alongside Nunjucks to dynamically insert data into the rendered page.

A simple example of page templates is to print the answer to a previous question.

For example:

{
"title": "What's your name?",
"path": "/full-name",
"components": [
{
"name": "applicantFullName",
"title": "What's your full name?",
"type": "TextField"
}
]
},
{
"path": "/greeting-page",
"title": "Hello, {{ applicantFullName }}"
},

The above snippet would ask the user for their full name, e.g. "Joe Bloggs". The following page would be titled "Hello, Joe Bloggs".

Why LiquidJS?

Nunjucks is the templating library of choice for frontends within Defra. Nunjucks templates are ultimately compiled down into Javascript code and executed, which opens the risk of user-defined code being executed.

LiquidJS is a safe alternative that provides very similar functionality to Nunjucks, but is not passed to eval or invoked as a function. This allows us to insert dynamic content into our forms using our configuration files, rather than requiring each team to hardcode it into the codebase.

The core codebase for forms-engine-plugin uses Nunjucks. LiquidJS is only used from the form definition JSON files.

Where page templates are supported

The following elements support LiquidJS templates:

  • Page title
    • jq path: .title
  • Form component title
    • jq path: .pages[].components[].title
    • Support for fieldset legend text or label text
    • This includes when the title is used in error messages
  • Html (guidance) component content
    • jq path: .pages[].components[].content
  • Summary component row key title (check answers and repeater summary)
    • Derived from component title

Template data

The data the templates are evaluated against is the raw answers the user has provided up to the page they're currently on. For example, given a YesNoField component called TKsWbP, the template {{ TKsWbP }} would render "true" or "false" depending on how the user answered the question.

The current FormContext is also available as context in the templates. This allows access to the full data including the path the user has taken in their journey and any miscellaneous data returned from Page events in context.data.

Templates should be single line JSON strings, where line breaks are not rendered and are defined as \n. Our recommendation is that template strings are edited separately to the form JSON, before being minified and copied into the JSON.

Built-in filters

There are a number of filters available to you from within the templates:

  • page - returns the page definition for the given path
  • field - returns the component definition for the given name
  • href - returns the page href for the given page path
  • answer - returns the user's answer for a given component
  • evaluate - evaluates and returns a Liquid template using the current context

Examples

Substituting a page title

Below is what a form may look like using page templates. It asks the user for their full name, then renders the following page with their name in the title. For example, "Are you in England, Joe Bloggs?".

"pages": [
{
"title": "What's your name?",
"path": "/full-name",
"components": [
{
"name": "WmHfSb",
"title": "What's your full name?",
"type": "TextField"
}
]
},
{
// This example shows how a page/component can use an answer to a previous question (What's your full name) in its title
"title": "Are you in England, {{ WmHfSb }}?",
"path": "/are-you-in-england",
"components": [
{
"name": "TKsWbP",
"title": "Are you in England, {{ WmHfSb }}?",
"type": "YesNoField"
}
]
}
]

Fully dynamic HTML snippets

You may want to do more than just add a dynamic value to the page title. Using the below example, you can create an entire HTML snippet and add it to a page.

Here is an example of a Liquid template that renders a page title, displays a link to a page called "are you in england" and prints out the answer to a question.

<p class="govuk-body">
{# Use Liquid's `assign` to create a variable that holds reference to the \"/are-you-in-england\" page #}
{%- assign inEngland = "/are-you-in-england" | page -%}
{# Use the reference to `evaluate` the title #}
{{ inEngland.title | evaluate }}<br>
{# Use the href filter to display the full page path #}
{{ "/are-you-in-england" | href }}<br>
{# Use the `answer` filter to render the user provided answer to a question #}
{{ 'TKsWbP' | answer }}
</p>

When using these kind of multi-line HTML snippets, you would benefit from our YAML-based form definitions that provide a better developer experience compared to JSON files.

If you choose to stick with JSON form definitions, the above template should be minified and inserted into the content field in the form definition example. E.g. quotes should be either replaced with ' or escaped \". Your IDE should do this automatically when pasting the into a JSON string, or a tool like https://www.freeformatter.com/json-escape.html can do it manually.

Full example of the minified and escaped component, which can be appended to the first example's JSON snippet.

{
// This example shows how a Html (guidance) component can use the available filters to get the form definition and user answers and display them
"title": "Template example for {{ WmHfSb }}?",
"path": "/example",
"components": [
{
"title": "Html",
"type": "Html",
"content": "<p class=\"govuk-body\">\r\n {# Use Liquid's `assign` to create a variable that holds reference to the \\\"\/are-you-in-england\\\" page #}\r\n {%- assign inEngland = \"\/are-you-in-england\" | page -%}\r\n\r\n {# Use the reference to `evaluate` the title #}\r\n {{ inEngland.title | evaluate }}<br>\r\n\r\n {# Use the href filter to display the full page path #}\r\n {{ \"\/are-you-in-england\" | href }}<br>\r\n\r\n {# Use the `answer` filter to render the user provided answer to a question #}\r\n {{ 'TKsWbP' | answer }}\r\n<\/p>"
}
]
}

Providing your own filters

Whilst forms-engine-plugin offers some out of the box filters, teams using the plugin have the capability to provide their own. See PLUGIN_OPTIONS.md for more information.

Using page templates with data from your own API

Page templates have access to {{ context.data }}, which is an attribute made available when a page event is triggered. It represents the entire response body from your API. To learn more about this, see our guidance on page events.