Solvedgutenberg Introduce concept of block templates for full site editing

Based on ideas previously explored in #3588 (and PR #4659) and recently discussed via @mtias in (see also particularly this comment by @westonruter), let's further specify how today's paradigm of hard-coded theme template files can transition to enable full-site editing using blocks.

There are two major technical pieces needed for this:

  1. An infrastructure to store blocks outside regular post content (in "block templates") and load these templates based on the current context (e.g. query variables and the main WP_Query instance). This issue is focused on introducing said infrastructure.
  2. Block types to cover the minimum requirements, both functionally and semantically, to cover what is currently handled by theme template files. These necessary block types are discussed via #15623 (and explored in PRs like #17207 and #17263).

Based on these two pieces, several UI decisions need to be made on how to expose these features to the user, and we will likely need some iterations to get this right. While we'll out of necessity probably touch on some of those ideas here, I suggest we focus on finding a solid under-the-hood solution first.

Here's the concrete suggestion:

  • Introduce a post type wp_template, of which each post represents a single top-level template. In other words, a template that renders an entire page's markup (to be differentiated from so-called template parts).
  • Fully maintain the existing template hierarchy, which is currently used to determine which theme /*.php template file to load. Basically, each wp_template post represents a specific one of those template files.
  • Provide a basic skeleton taking care of printing the non-visual markup (<html>, <head>, wp_head hook, <body> opening and closing tag, etc.). Visually, it will render the actual blocks from the current template.
  • Skip the templates provided by themes entirely, since a hybrid approach would present unnecessary obstacles and confuse the user. Since this will drastically change how themes need to be developed, let's put it behind an experimental flag for now. Eventually, it could possibly become something that requires explicit add_theme_support(), at least in a transitional phase.

Based on this, a few further thoughts:

  • We will also need a way to reuse certain "template parts". This could be accomplished with e.g. the existing reusable blocks feature, or a separate post type wp_template_part, or an alternative variant of reusable blocks that only applies to template content (i.e. not individual post content, for better separation).
  • WordPress theme developers are typically familiar with the template hierarchy, and thus will easily be able to understand the block-based version of it. Given that the necessary block types are present, porting existing templates over will be trivial.
  • For the eventual UX, we probably should not expose template identifiers (such as index, category, singular, single-product, etc.) to the user because these are rather technical. While every theme developer has been exposed to them, they have (for a reason) never been used in WordPress UI. We will need to find a solid way for a user to understand which template they are editing, and for them to decide in which context/scope they'd like to edit the template. For an initial (experimental) implementation, it's fine to just expose block templates with these names.
  • We also need to think about how themes can provide default block templates. A theme could for example provide general layouts to choose from when creating a new wp_template post. Or it could include default block content (in place of today's template files) to use even if no user-created variant of that template exists as a wp_template post. Or on theme activation, these wp_template posts could be automatically created.
  • Developing a theme will be greatly simplified by this methodology. A theme will be responsible for the style and default layouts/templates, and most likely also it will still control some level of user-facing customization options (whether using the Customizer or an alternative interface). However, several other theme features today will become obsolete. In a block-based world, something like custom-header, widget areas, or nav menu locations probably don't make sense anymore. Other "features" like adding theme support for title-tag could potentially be automatically enabled.
27 Answers

✔️Accepted Answer

Since we already did some Full Site Editing work on, I’ll try to convey some of the things we’ve tried so far and some of our takeaways. I’d like to preface that with a comment that our work was intentionally scoped down for two reasons:

  • We wanted to have a smaller proof-of-concept solution that we could test with our users and gather feedback that would inform our future work (and possibly Core).
  • We decided to develop this as a plugin, which posed additional limitations, since this work ultimately requires Core changes.

During our work we experimented with a couple of approaches:

  1. Hijacking the template hierarchy from the plugin.

    • Pro: it works with existing themes without needing to modify them.
    • Con: imposing one template for all themes introduces visual and structural inconsistencies.
    • Note: we also looked into overriding get_template_part, but in case of Header it pulls in <head> tag too, and not just <header> content.

  1. “Blank theme” approach - essentially a theme with only index.php defined that replicates the template hierarchy using predefined page templates (in wp_template CPTs).

    • Pro: allows the most freedom when editing page templates in Gutenberg and assigning them to specific posts/categories.
    • Con: backward incompatible with existing themes.
    • Note: this also redefines the concept and area of responsibility of future themes. For more details check out this exploration from @Copons: Automattic/wp-calypso#32865

  1. Adapting existing themes to make them FSE-aware. If they detect that FSE is active, they would delegate template part rendering to it, otherwise they fall back to their default non-FSE content.

    • Pro: can be made to work with existing themes.
    • Con: themes have to be updated to make them FSE aware; introduces slight coupling between themes and FSE functionality.

We ended up using (3) in the end, because it seemed like the most seamless way to get what we need for our scoped-down solution. While this worked for us, it’s not necessarily the best long-term approach for Core, but some pieces could still be reused. This code now lives in:

Populating and storing template data

Our initial template data population happens on after_switch_theme hook, and this can work regardless of the final data storing approach. It provides a way for each theme to register its own specific template data. In order to avoid duplicating this code across all themes that want to support FSE, this functionality is now implemented in FSE plugin, but it seems like something that could be incorporated into core down the road. As a side note, we also attempt this on plugin activation, to cover the case when FSE supporting theme is already active. Overall it follows these steps:

  1. Data population executes on after_switch_theme or register_activation_hook.
  2. We check if template data for this particular theme is already present and bail if it is.
  3. Fetch template data for the theme being activated from a centralized API.
  4. Populate corresponding template CPTs with fetched data.
  5. Mark CPTs with corresponding theme specific taxonomy terms (e.g. maywood_header or similar).

In our case the benefit of (3) is that we don’t have to release a new theme version if we want to publish a fix or update to template data.

As a result of (2) and (5), it’s possible to preserve existing customizations to template parts across theme switches. For example, if you want to just try out a couple of new themes on your site, and then revert to your original one, the previous theme specific customizations to your header would be restored.

For storing the data, we are now only using wp_template_part CPT, but that’s just because our solution didn’t require more. When we tried to envision what core is going to do and align with that a couple of months ago, we leaned toward the wp_template and wp_template_part split, as opposed to just wp_template CPT with hierarchical taxonomies. To clarify, both options seem viable from the implementation standpoint, but it seems like the former requires less wiring, might perform slightly better, and could better aligns with existing WP concepts and infrastructure. Some benefits in my opinion:

  1. There is a nice parallel with terminology in existing template hierarchy (page templates and template parts).
  2. We are trying to model a tree structure, and that’s easier to enforce with the above split. In order to prevent it from becoming a cyclic graph, that could result in infinite loops in editing and rendering context, our query would have to be more complex and filter out page templates in some contexts (e.g. we want to prevent template A containing template B, which also contains A).
  3. Similar to (2), designating a template part as a full page template wouldn’t make much sense, again requiring additional code, and likely another table join with taxonomies to filter them out (depending on implementation). For example: assigning the Header template part as the Homepage template doesn't make sense because a "full-fledged template" would entail several template parts (header, footer, post content etc.).

While all of the above can easily be worked around, they might hint that we are trying to clump together two conceptually different entities under the same roof, and we might discover additional work on account of that down the road too.

It seems to me like the model would become much simpler if we enforced no nesting of entities of the same type, both for wp_template and wp_template_part. So page template could only contain template parts, and template parts would in turn be composed of static, dynamic, and reusable blocks. I’m still not sure if this is too constraining, and it definitely requires some more thought. So something along the following lines:

FSE templates (4)

P.S.: I tried to condense this as much as possible, but it still turned out a bit longer than expected - sorry about that. :) If anything is unclear because of that I'd be happy to clarify. Also, if there is any way that we can further help the core FSE work based on what we currently have on (be it feature testing or user feedback) feel free to let me know.

Kudos to @Copons for reviewing this and suggesting edits.

Other Answers:

If I may chime in, as a person who worked hard on the Blank Theme experiment, and struggled immensely in explaining it to peers and stakeholders alike: we desperately need the best and clearest terminology we can come up with.

wp_template, wp_template_part, template hierarchy, template files: it's painfully obvious to me that the "template" word is overloaded of meanings!

I don't have a good answer, and I know this seems like bikeshedding, but if we manage to figure out some unambiguous terms, then FSE will become 100% easier to discuss, and the project itself will move forward quicker.

  • We also need to think about how themes can provide default block templates. A theme could for example provide general layouts to choose from when creating a new wp_template post. Or it could include default block content (in place of today's template files) to use even if no user-created variant of that template exists as a wp_template post. Or on theme activation, these wp_template posts could be automatically created.

Yes, a theme should provide an templates block data that get merged with wp_template posts into the template hierarchy. Whatever is defined in wp_template posts should override what is defined in the theme. As soon as a wp_template is removed, then the theme-provided template data should be used. I don't think the theme-provided template data should get inserted as wp_template posts upon activation.

When creating a wp_template post, then you could decide to use one of the theme-provided templates block data as the starting point. Something like what @jeremyfelt shows in a tweet.

We may want to consider keeping track of which theme was active when a given wp_template post was created. While ideally all themes that support wp_template should be able to simply swap out the template, some themes may have stylesheets that expect certain template block configurations. If the wp_template posts were tagged for the active theme, then it could provide a way to filter the templates based on this. This comes to mind based on the Additional CSS feature in the Customizer, which stores CSS in a custom_css post type, but the CSS is theme-specific so each theme gets their own dedicated custom_css post. We were thinking at some point to provide a way to import CSS from one theme into another, and a similar idea may apply here for templates. In any case, the default case for wp_template posts is that they should persist by default when switching themes.

Most of the architecture work for this issue has already landed. I'm going to close this issue now, let's focus on smaller targetted issues now.

More Issues: