Plan how to evolve code component overrides

Created on 17 April 2025, about 2 months ago

Experience Builder is designed to facilitate comprehensive theming across an entire site. We previously introduced an experimental concept and solution to override the output of a small set of blocks using JavaScript components: site branding, breadcrumbs, and menu blocks. The implementation was meant to be experimental.

Create an ADR that outlines alternatives and documents our decision on how we plan to evolve this implementation.

๐ŸŒฑ Plan
Status

Active

Version

0.0

Component

Theme builder

Created by

๐Ÿ‡ณ๐Ÿ‡ฑNetherlands balintbrews Amsterdam, NL

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @balintbrews
  • ๐Ÿ‡ฌ๐Ÿ‡งUnited Kingdom AaronMcHale Edinburgh, Scotland
  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ
  • ๐Ÿ‡ณ๐Ÿ‡ฑNetherlands balintbrews Amsterdam, NL

    Crediting people who were part of the discussion that led to the ADR at Drupal Dev Days in Leuven.

  • Pipeline finished with Canceled
    about 2 months ago
    Total: 118s
    #476044
  • ๐Ÿ‡ณ๐Ÿ‡ฑNetherlands balintbrews Amsterdam, NL
  • Pipeline finished with Success
    about 2 months ago
    Total: 952s
    #476045
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States effulgentsia

    I really like the option 2 proposal that's in the ADR. My one nit though is I don't think we should do it as a custom hook, but instead as a plain async function that's passed to React 19's use() API. In other words, instead of:

    import { useMenuData } from '@/lib/menu';
    
    const NavigationMenu = ({ menuId, level }) => {
      const menuData = useMenuData(menuId, { level });
      // ...
    }
    

    to do:

    import { use } from 'react';
    import { fetchMenuData } from '@/lib/menu';
    
    const NavigationMenu = ({ menuId, level }) => {
      const menuData = use(fetchMenuData(menuId, { level }));
      // ...
    }
    

    This would then also work for other UI frameworks when we add support for them in the future, which we might do soon as part of the CLI work. For example, in Svelte:

    {#await fetchMenuData(menuId, { level }) then menuData}
    ...
    {/await}
    

    One hiccup with this approach though is that preact/compat doesn't implement the use() function. I opened https://github.com/preactjs/preact/issues/4756, but we can't count on that getting done in time. However, I think we can work around that by implementing our own use() function until such time as Preact implements the proper one, and ours can sidestep the need to work with Suspense, because we can rely on either server-side prefetching or async Astro island hydration to make sure we don't hydrate the component until we've already fetched the data and can return it synchronously by the time use() is called.

  • Pipeline finished with Success
    about 1 month ago
    #491012
  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

    Reviewing at last ๐Ÿ˜ฌ

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

    Sorry for the long wait. Based on DDD discussions, I expected this to be much bigger than it turns out to be, I should've reviewed this sooner ๐Ÿ™ˆ

    @effulgentsia's remarks in #10 are squarely on the front-end implementation side of this new component source plugin, which I have no strong opinions about.

    I reviewed this from a XB data model + component source architecture + reliability + ecosystem reach POV.

    The ADR simultaneously does not go into detail about the challenges of the most common use case (menu blocks) and also doesn't really explain how we'd make other use cases (think: arbitrary other block plugins such as the branding block). We discussed much of this at length in person at DDD Leuven.

    I think this ADR is a great start, but I think there's a few crucial things that are too open-ended for this ADR to be mergeable. I'm happy to be overruled if all @balintbrews, @longwave, @effulgentsia and @lauriii all think this is sufficiently precise. But I would like to see more detail, because at least to me it's not clear how this proposes to implement "block overrides" for e.g. the breadcrumbs or site branding blocks. ๐Ÿ˜‡๐Ÿ™

    • โœ… Always passing the entire menu tree to the component is not good enough: different menus want to show different slices or subtrees of a menu โ€” for example only the top level, only the first 2 levels, starting at the 3rd level etc.

      The obvious solution to support parametrization: a HTTP API to fetch menu links, for which Drupal core has the โ†’ endpoint.

      ๐Ÿ‘‰ The ADR covers this: +

    • โŒ The ADR does not explain the code component developer/author DX for knowing the shape of the data they would receive.
    • โŒ The ADR does not explain how the breadcrumbs block would work in option 2. Drupal has no HTTP API to fetch the results of a service (the breadcrumb service in the server-side container). Is building such a HTTP API part of the envisioned scope?
    • โŒ The ADR does not explain how the site branding block would work in option 2. There's no HTTP API to fetch (simple) config (the site name + slogan). Core's JSON:API and REST modules both do not support that. Is building such a HTTP API part of the envisioned scope?
    • ๐Ÿค” The ADR does not state whether this would be a new component source plugin, or whether it's just the existing "code component" source but with additional APIs (for data fetching) available. If it's the same component source plugin, how will we ensure that the data-fetching code components end up in the UI library? (See \Drupal\experience_builder\Entity\Component::computeUiLibrary().)

      Can you confirm that the intent in all 3 options is to implement a new "block override" ComponentSource plugin, which just happens to share a lot of infrastructure with the existing "code component" ComponentSource plugin, but is about data fetching (and hence ) instead of providing props/slots?

  • ๐Ÿ‡ณ๐Ÿ‡ฑNetherlands balintbrews Amsterdam, NL

    Wim and I will meet tomorrow to talk things through, then I will make the necessary adjustments.

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

    Productive meeting with @balintbrews just now ๐Ÿ˜„ He'll add great clarifications on the ADR ๐Ÿ˜Š๐Ÿ‘

Production build 0.71.5 2024