Skip to main content
Last updated: 11 Dec 2024

frontend: Building blocks for flexible content

The "landing-page" document type will allow content designers to build pages with a flexible design by creating a "block" that renders certain parts of the page.

Blocks can be nested within each other. A top-level block is a horizontal chunk of the page.

Blocks are rendered in the order that they appear in the content item.

Blocks are added to the content item in the details hash. The landing page editor is currently a work in progress. At time of writing, the config for blocks need to be added in YAML format in Whitehall. This YAML config is then merged into the details hash when the landing page is saved and published.

The YAML config must include a top-level blocks element.

blocks:
- type: block_type

It may optionally include other top-level elements:

  • a breadcrumbs element, an array of title/href pairs which will be used to create a breadcrumb list.
  • an extends element to inherit configuration from another landing page (See Extending a page (Whitehall Only)).
  • an image element. This has one sub-element (url) which should be the url for an image to use with social media metadata.
  • a navigation_groups element for use by blocks (See Navigation Groups)
  • a theme element which will set the general appearance of the page. Valid themes are default or prime-ministers-office-10-downing-street) - if no theme is specified, default will be used (See Themes)

Block configuration is designed to be as flexible as possible, so blocks can be nested inside other blocks. An example configuration for each type of block is shown below.

Types of Block

Each block is rendered using its own partial template. The full list of available types of block available can be found by looking at the contents of: app/views/landing_page/blocks.

Blocks can be given a full width light grey background by setting full_width_background: true in the content item. This will not change the layout of the block itself. Note that sequential blocks given this option will fit together without a gap, because backgrounds are often required on areas of the page consisting of more than one block.

Simple blocks

Simple blocks generally render one component or "thing". They can either be rendered directly or as the children of compound blocks.

A wrapper around the action link component.

- type: action_link
  text: "Learn more about our goals"
  href: "/landing-page/goals"

Big Number

A wrapper around the Big number component

- type: big_number
  number: £75m
  label: amount of money that looks big

Document List

A wrapper around the Document list component with a title that will only appear if there are any items in the list (if the list is entirely empty the heading will not appear either)

The document list can either be populated with the most recent content tagged to a taxon or from a hard-coded list.

If the taxon_base_path is provided and the taxon exists and it has content tagged to it, the document list will be populated with that content. If not, it will default to the hard-coded list.

If the heading is provided that will be used, otherwise it will default to the hard-coded heading.

- type: document_list
  heading: My Favourite Things
  taxon_base_path: /government/government-efficiency-transparency-and-accountability
  items:
  - text: An example link
    path: https://www.gov.uk
    document_type: Press release
    public_updated_at: "2016-06-27 10:29:44 +0000"
  - text: Another example link
    path: https://www.gov.uk
    document_type: News article
    public_updated_at: "2021-01-16 11:34:12 +0000"
  - text: A third example link
    path: https://www.gov.uk
    document_type: Consultation
    public_updated_at: "2024-02-01 09:00:11 +0000"

Govspeak

A wrapper around the Govspeak content component. Content for this block can be written as govspeak markdown or HTML, depending on how the block is added.

When added to a YAML file within the frontend application content must be written in HTML.

- type: govspeak
  content: |
    <h3>Lorem heading</h3>
    <p>Korem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eu turpis
    molestie, dictum esta, mattis tellus.</p>

When added through Whitehall content can either be written in HTML or govspeak markdown, but content-type must be specified if using govspeak markdown.

- type: govspeak
  content:
    content_type: text/govspeak
    content: |
      ### Lorem heading

      Korem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eu turpis
      molestie, dictum esta, mattis tellus.

Heading

A wrapper around the Heading component. This block uses content where the component uses text for the title text key. This is so that headings can appear in search (only values inside a content key will be indexed when being published from Whitehall - see Indexing block content in search)

If an id is added, the heading can be referenced in an anchor link.

- type: heading
  id: heading-id
  content: Porem ipsum dolor

Image

A simple image.

3 versions of the image need to be supplied. This is also true for blocks like Featured and Hero that contain images.

NB: Like Hero if you are adding this block in Whitehall you do not need to manually specify the _x2 variants.

NB: Unlike Hero this image usually displays 3 different versions of the same image, with the same ratio, so although in Whitehall you have to specify the three tags (desktop, mobile, and tablet), you can usually use the same markdown image tag.

If needed, an optional theme_colour integer value can be specified, which gives the image a border top utilising the theme colour you specified.

- type: image
  theme_colour: 1
  image:
    alt: "Placeholder alt text"
    sources:
      desktop: "landing_page/placeholder/desktop.png"
      desktop_2x: "landing_page/placeholder/desktop_2x.png"
      mobile: "landing_page/placeholder/mobile.png"
      mobile_2x: "landing_page/placeholder/mobile_2x.png"
      tablet: "landing_page/placeholder/tablet.png"
      tablet_2x: "landing_page/placeholder/tablet_2x.png"

The top-level navigation. It supports multiple headings, each with a row of links. The only supported keys are the type key and the navigation_group_id key, which should point to an id of a navigation group to use for the menu items.

- type: main_navigation
  navigation_group_id: Top Menu

Quote

A blockquote.

- type: quote
  text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc vulputate libero et velit interdum, ac aliquet odio mattis class"
  cite: "Lorem ipsum dolor sit, 28 September 2024"

A wrapper around a Heading component and a Share links component.

The heading is hard-coded in the share links view and cannot be configured in the block.

- type: share_links
  links:
    - href: /twitter-profile
      text: Twitter
      icon: twitter
      hidden_text: Follow us on
    - href: /instagram-profile
      text: Instagram
      icon: instagram
      hidden_text: Follow us on
    - href: /flickr-profile
      text: Flickr
      icon: flickr
      hidden_text: Follow us on
    - href: /facebook-profile
      text: Facebook
      icon: facebook
      hidden_text: Follow us on
    - href: /youtube-profile
      text: YouTube
      icon: youtube
      hidden_text: Follow us on

Side Navigation

A navigation item suitable for use in a sidebar. It supports a single column of links. The only supported keys are the type key and the navigation_group_id key, which should point to an id of a navigation group to use for the menu items.

- type: side_navigation
  navigation_group_id: Sidebar

Statistics

A wrapper around the Chart component. The csv_file key can be an uploaded file (from /lib/data/landing_page_content_items/statistics), or (preferably) an attachment from the content item, in which case the filename of the attachment will be matched to the csv_file key.

- type: statistics
  title: "Chart to visually represent data"
  x_axis_label: "X Axis"
  y_axis_label: "Y Axis"
  csv_file: "data_one.csv"
  data_source_link: https://www.ons.gov.uk/economy/grossdomesticproductgdp/timeseries/ihyq/qna

For a minimal chart add minimal: true to the block config. This will output a simplified graph with no interactivity.

- type: statistics
  title: "Chart to visually represent data"
  x_axis_label: "X Axis"
  y_axis_label: "Y Axis"
  csv_file: "data_one.csv"
  data_source_link: https://www.ons.gov.uk/economy/grossdomesticproductgdp/timeseries/ihyq/qna
  minimal: true
  minimal_link: /landing-page/task

Compound blocks

Compound blocks generally render more than one component and can contain nested blocks. The nested blocks can be simple blocks or themselves compound blocks

Box

A box block renders its content value as a Heading component. If an href: is specified, the heading will link to that location. Subblocks are laid out vertically beneath the heading.

Box blocks have a light grey background and can be styled with a predefined top border for colour-coded content using the theme_colour property.

- type: box
  content: This is a heading
  href: https://www.gov.uk
  theme_colour: 1
  box_content:
    blocks:
      - type: govspeak
        content: |
          <p>Yorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc vulputate libero et velit interdum, ac aliquet odio mattis class.</p>

Card

A card block renders its card_content value as a Heading component, followed by the subblocks laid out vertically, followed by an image (if specified - at time of writing the image in the card block can not be taken from the content item and must be present in frontend).

- type: card
  card_content:
    blocks:
    - type: heading
      content: Rorem ipsum dolor sit amet, consectetur adipiscing elit.
      path: http://gov.uk
    - type: statistics
      x_axis_label: "X Axis"
      y_axis_label: "Y Axis"
      csv_file: "data_one.csv"
      data_source_link: https://www.ons.gov.uk/economy/grossdomesticproductgdp/timeseries/ihyq/qna

A featured block renders either a vertical column of blocks on the left of a large (but not full-width) image (it uses a picture element to allow multiple image versions for different devices) or a block of text without an image. The latter can be rendered by omitting the image value as demonstrated in the second example below.

- type: featured
  image:
    alt: example alt text
    sources:
      desktop: "landing_page/placeholder/desktop.png"
      desktop_2x: "landing_page/placeholder/desktop_2x.png"
      mobile: "landing_page/placeholder/mobile.png"
      mobile_2x: "landing_page/placeholder/mobile_2x.png"
      tablet: "landing_page/placeholder/tablet.png"
      tablet_2x: "landing_page/placeholder/tablet_2x.png"
  featured_content:
    blocks:
      - type: heading
        content: Our tasks
      - type: govspeak
        content: |
          <p>Lorem ipsum dolor sit amet. In voluptas dolorum vel veniam nisi et voluptate dolores id voluptatem distinctio. Et quia accusantium At ducimus quis aut voluptates iusto aut esse suscipit.</p>
- type: featured
  featured_content:
    blocks:
      - type: heading
        content: Our tasks
      - type: govspeak
        content: |
          <p>Lorem ipsum dolor sit amet. In voluptas dolorum vel veniam nisi et voluptate dolores id voluptatem distinctio. Et quia accusantium At ducimus quis aut voluptates iusto aut esse suscipit.</p>

Hero

A hero block displays a full screen width image with an optional text box, that can contain further blocks.

The text box will be two thirds width (default, intended for use at the top of a page) or one third for use part way down a page (configured using theme: middle_left).

Like the featured block, it uses a picture element to allow multiple image versions for different devices.

As hero images are intended to be decorative, it's not possible to configure them with alt text.

Structure

The YAML structure for hero images is slightly different depending on whether the content item is in Content Store (or a hardcoded YAML block which has to match the Content Store format), or in Whitehall.

In content store or when hardcoded, the block should look like this:

- type: hero
  image:
    sources:
      desktop: "landing_page/placeholder/desktop.png"
      desktop_2x: "landing_page/placeholder/desktop_2x.png"
      mobile: "landing_page/placeholder/mobile.png"
      mobile_2x: "landing_page/placeholder/mobile_2x.png"
      tablet: "landing_page/placeholder/tablet.png"
      tablet_2x: "landing_page/placeholder/tablet_2x.png"
  hero_content:
    blocks:
      - type: heading
        content: Rorem ipsum dolor sit

When authored in Whitehall, uploaded images should be referred to using markdown syntax.

- type: hero
  image:
    sources:
      desktop: "[Image: desktop.png]"
      mobile: "[Image: mobile.png]"
      tablet: "[Image: tablet.png]"
  hero_content:
    blocks:
      - type: heading
        content: Rorem ipsum dolor sit

Whitehall will automatically create separate _2x images for high density screens.

Note that the [Image: desktop.png] syntax must be wrapped in quotes ("[Image: desktop.png]") to ensure that YAML treats it as a string and not an array.

Layout blocks

Layout blocks are similar to compound blocks in that they contain nested blocks. They are used to arrange blocks on the page, and to make it easier to apply styling to the blocks.

Blocks Container

A blocks container is used as an empty unstyled parent container to hold other elements. It is used when we don't want to want to create a row/grid layout to contain nested blocks. It is for situations where the grids and columns have already been defined, and you are nesting other blocks within them (see second example).

It does nothing except contain other blocks and is only required as the top level element within columns created by two column layouts. Without a blocks container, there’s no way to add more than one sub-block in the column or arrange blocks on top of each other in a single column.

- type: blocks_container
  blocks:
  - type: govspeak
    content: |
      <p>Korem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eu turpis molestie, dictum esta, mattis tellus.</p>
  - type: statistics
    title: "Chart to visually represent data"
    x_axis_label: "X Axis"
    y_axis_label: "Y Axis"
    csv_file: "data_one.csv"
    data_source_link: https://www.ons.gov.uk/economy/grossdomesticproductgdp/timeseries/ihyq/qna
  - type: govspeak
    content: |
      <p>Korem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eu turpis molestie, dictum esta, mattis tellus.</p>

Nested blocks container:

- type: two_column_layout
  theme: one_third_two_thirds
  blocks:
  - type: govspeak
    content: <p>Left content!</p>
  - type: blocks_container
    blocks:
      - type: govspeak
        content: |
          <h2>This is a heading</h2>
          <p>Lorem ipsum...</p>
      - type: action_link
        text: "See the missions"
        href: "todo"

This example uses the blocks_container for the element type in the second two-thirds width column. Without this empty unstyled container we'd have no way of inserting multiple different blocks in the column.

Columns Layout

Columns layout spreads its blocks out horizontally. The columns property determines how many columns will be in each row, and can accept the following values:

  • columns: 2 Two columns, each taking up half of the available space.
  • columns: 3 Three columns, each taking up one-third of the available space.
  • any other value for columns will have no effect: all child elements will render the full width of this container.

If columns is missing entirely, three columns will be rendered by default.

In the following example, the first two big_number blocks will be side-by-side on the first row and the third block will drop down onto the next row, taking up the left hand column.

- type: columns_layout
  columns: 2
  blocks:
  - type: big_number
    number: £75m
    label: amount of money that looks big
  - type: big_number
    number: 100%
    label: increase in the number of big_number components added to the columns at this point
  - type: big_number
    number: £43
    label: Cost of a cup of coffee in Covent Garden

Two Column Layout

A two column layout takes one or two blocks and a theme to determine the column layout. These options are:

  • one_third_two_thirds
  • two_thirds_one_third
  • two_thirds_right - provides the same layout as one_third_two_thirds but content for the left column is not required

Theme: one third / two thirds

- type: two_column_layout
  theme: one_third_two_thirds
  blocks:
  - type: side_navigation
    navigation_group_id: Sidebar
  - type: blocks_container
    blocks:
    - type: govspeak
      content: |
        <p>This content will be in a two thirds column.</p>

Theme: two thirds / one third (note that content is not required for the one third column)

- type: two_column_layout
  theme: two_thirds_one_third
  blocks:
  - type: blocks_container
    blocks:
    - type: govspeak
      content: |
        <p>This content will be in a two thirds column.</p>
  - type: govspeak
      content: |
        <p>This content will be in a one third column.</p>

Theme: two thirds right

- type: two_column_layout
  theme: two_thirds_right
  blocks:
  - type: blocks_container
    blocks:
    - type: govspeak
      content: |
        <p>This content will be in a two thirds column, offset one third from the left of the page.</p>

Only content in blocks that have a label of content will be indexed in site search.

blocks:
- type: govspeak
  content: |
    ## This will be a searchable header
    here's some content that's searchable
- type: button
  text: Press me (not searchable)
- type: header
  content: searchable header

In the example above only the text in the govspeak and header blocks will be searchable.

Utility blocks

Utility blocks exist to support development. They will not render on the published view of the page, although they may render on the draft view of pages.

Block Error

This is a special type of block that handles errors in other blocks. If a block raises an error during initialization, the block factory will turn it into an error block. This will only render in draft view, where it will display the error in place. It will not show on the live site.

The top-level key navigation_groups contains an array of items with the keys id, name and links. The id is used to identify the group for navigation blocks like main navigation and side navigation, which reference a navigation group with their navigation_group_id key. The name key is used for the main button text (if the navigation collapses behind a button). The links key contains an array of text keys. These text keys represent headings that are rendered on the navigation element. These text values also contain a child links item, with links being an array of links related to that heading.

navigation_groups:
- id: Top Menu
  name: Name for the main menu button
  links:
  - text: First heading
    links:
    - text: Landing page
      href: /landing-page/homepage/
    - text: Another link
      href: /landing-page/example/
  - text: Second heading
    links:
    - text: Goal 1
      href: /goal-1
    - text: Goal 2
      href: /goal-2

Themes

There are two types of theme key. If theme appears at the top-level, it affects the structure and layout of the whole page, and must be on the list below. Some blocks have their own themes, which have their own set of valid values and only affect that component. These two types of theme key work independently.

Valid top-level themes:

  • default (or no theme specified): a general-purpose page, with the gov.uk header and footer.
  • prime-ministers-office-10-downing-street: similar to default, but has a dark background/inverted text for the breadcrumbs, and an inverted version of the No. 10 header, with the crest of the Prime Minister's office.

Extending a page (Whitehall Only)

When editing a page in Whitehall, you may wish to use data from another flexible content page. Adding the top-level key extends, and the path of another landing page, will cause the data from the current block to be merged over the data from the extended block before publishing. This is a shallow merge (ie only top-level values are checked), and is currently used so that navigation groups can be specified in one page and used by sub-pages.

If you have a homepage (at /landing-page/homepage) like this:

navigation_groups:
- id: Top Menu
  links:
  - text: Homepage
    href: /landing-page/homepage
  - text: Child 1
    href: /landing-page/child
  - (etc)
blocks:
- type: main_navigation
  navigation_group_id: Top Menu
- type: govspeak
  content: <p>Hello!</p>

and a child page (at /landing-page/child) like this:

extends: /landing-page/homepage
blocks:
- type: main_navigation
  navigation_group_id: Top Menu
- type: govspeak
  content: <p>Hello from the child!</p>

...the navigation_groups element will be copied into the child page when it is saved (allowing you to specify a shared menu in one page and simply republish all the child pages in Whitehall).

The extends keyword copies over everything from the parent to the child. It then replaces parent elements with what's in the child if a duplicate top-level element exists in the child. For example, if the child page didn't specify any blocks, those too would be copied over from the parent. That's because extends does a shallow merge of everything in the details element of the parent. This means that it is possible to add more top-level elements that can be shared across child pages.

The merging of elements is only visible in the content item. In the Whitehall editor you'll still only see the extends keyword, but in the content item the navigation_groups would be expanded on all child pages as well as the parent:

"navigation_groups": [
  {
    "id": "Top Menu",
    "links": [
      {
        "href": "/homepage",
        "text": "Home page"
      },
      {
        "href": "/goals",
        "text": "Goals"
      },
      {
        "href": "/tasks",
        "text": "Tasks"
      }
    ]
  }
]

Republishing all Landing Pages

If a navigation_group is modified on a parent page, all of the child pages will need to be republished to pick up the changes.

Whitehall has a UI to help you do this.

  1. Go to "More" in menu (top right)
  2. Click on "Republish content"
  3. Scroll down to "Bulk republishing"
  4. Scroll to "All by type" and click "Republish"
  5. Select "LandingPage" from the drop-down and click "Continue"
  6. Provide a reason for the republish and click "Confirm republishing"
  7. You should see a Success message with "All by type 'LandingPage' have been queued for republishing"

As a last ditch option there is also a rake task that does the same thing:

rake publishing_api:document_type["LandingPage"]