Zürich Tourism on a headless setup with improved performance

Case summary

Experience the city of Zurich virtually: After migrating from Drupal 7 to Drupal 9, a headless CMS was integrated, bringing optimized performance.

Case description

The website of Zurich Tourism needed a technical upgrade for greater flexibility in the frontend and to improve site performance. We were able to deliver a decoupled CMS that accommodates four different languages and multiple domains.

Case goals and results

The overall goal of the relaunch of the Zurich Tourism platform was to enable the city of Zurich to be discovered and experienced virtually. The project started back in 2020, with the migration project from Drupal 7 to Drupal 9. In 2022, a headless setup was adopted in full, optimizing the website's performance.


The Zurich Tourism website displays a large amount of content that can regularly change. Although the pages are cached on a Content Delivery Network (CDN), small changes can invalidate certain pages (e.g. changes to a menu item). To improve the performance of the page, individual components are also cached. These include, for example, the components for the logo, the menu, the footer, teasers, and the page content. These components are cached as a generated markup in the Node.js app. If a new menu item is then added, resulting in all pages in the cache being invalidated, only the menu component has to be regenerated once. The next step involves completely assembling each page from cached components. This reduces the average response time for an uncached page from around 800ms to less than 100ms. The more pages are called up, the quicker it is.

A Drupal module was also developed. It ensures that when changes are made to the CMS content, the corresponding components in the frontend app are removed from the cache. This works because each component "knows" what content it displays (cache tags). This function is already available in a classic setup, where Drupal also takes care of rendering(render cache). We have adapted this concept to a headless setup and implemented it.

This approach combines the advantages of hybrid rendering (server-side rendering and subsequent client-side rendering) with those of static site generation (all pages are generated as HTML in advance). The disadvantage of the latter approach is that, as a rule, each page has to be completely regenerated. However, in this combined architecture, changes can be published immediately and are updated in under a minute.

All data is loaded from the frontend via GraphQL. Since these cannot be cached by default (POST request), we have developed a new Nuxt module that automatically provides all GraphQL queries as GET REST endpoints, which are then cached on the CDN.

**Layout Builder**

The Drupal Layout Builder is a site-building tool that allows complete flexibility in creating content pages. Different layouts (e.g. one column, three columns, teaser lists, sliders, etc.) can be added and blocks (e.g. text, images, teasers) can be placed in them.
However, the Layout Builder UI provided by Drupal is complex to use and requires a lot of clicks to perform the simplest of actions. In addition, the editing display does not fully correspond to the final display.
Thanks to the headless architecture selected, we were able to fully implement the user interface of the Layout Builder as a Vue app. The content displayed during editing is the same as what website visitors see. The Layout Builder has been adapted to the editors' requirements:

● No hidden functions: Every function is accessible in one click
● Quick access to add new sections
● Instant preview mode to view the page in its final state without having to publish it
● Keyboard shortcuts (copy, paste, duplicate, etc.)
● Any content can be easily reused on multiple pages, including translations. Many landing pages are based on a theme and a teaser can be placed on 10 different pages, for example
● Reusable content can be searched using the full-text search. In addition, a preview is immediately visible
● Multi-channel: Content is published simultaneously on zuerich.com and via the Zurich Guide app. There is an option to publish sections and blocks only on the website, on the app, or everywhere. A toggle enables the content of the app to be previewed on the web
● Full integration and backward compatibility with Drupal


The project was one of the first headless projects in our team. As a result, some fundamental themes had to be redesigned and developed. The aim was to develop sustainable concepts that could be used in other projects and by the community. In addition, some components had to be redeveloped due to the headless approach especially those that are normally available from Drupal out of the box (e.g. cache tags).

Community contributions

In this project, we contributed to various modules via patches on Drupal.org. For Zürich Tourismus, we focused on the GraphQL ecosystem by implementing our headless version of the Layout Builder and therefore published the following open-source modules to the ecosystem:

* GraphQL Layout Builder: allows you to build decoupled websites using GraphQL and the full power of layout builder. (https://www.drupal.org/project/graphql_layoutbuilder)
* Layout Builder iFrame modal: allows you to render Layout Builder edit forms in an iframe, using the admin theme (https://www.drupal.org/project/layout_builder_iframe_modal)


Hosting is done via platform.sh

Why should this case win the splash awards?

The innovative caching approach significantly improved the performance of the site. Furthermore, with the GraphQL Layout Builder, a module was developed that generates extreme added value in the editor experience and significantly facilitates handling.

Case video