Skip to content

Global Elements: Reusable Content of Blocks - #22448

Merged
leekelleher merged 73 commits into
v19/devfrom
v18/feature/reusable-block-content
Jun 23, 2026
Merged

Global Elements: Reusable Content of Blocks#22448
leekelleher merged 73 commits into
v19/devfrom
v18/feature/reusable-block-content

Conversation

@kjac

@kjac kjac commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

Description

Part of Global Elements — Phase 2. This adds Reusable Content of Blocks: a block in any Block Editor (Block List, Block Grid, Single Block, and Rich Text blocks) can now reference a shared Element from the Library instead of holding its own local content. Editing the Library Element updates every block that references it.

What you can do

  • Insert from Library — the block catalogue has a new Library tab for picking an existing Library Element to insert as a referenced block.
  • Transfer to Library — promote an existing local block to a reusable Library Element (you give it a name), so it can be reused elsewhere.
  • Disconnect from Library — break the reference on a single block. This creates a local copy of the Element's content; the Library Element itself is left unchanged.
  • Edit in place — edit a referenced Element's content directly from within the block editor.
  • Publish awareness — referenced blocks surface the publish state of the underlying Library Element (e.g. an unpublished indicator).
  • Usage visibility — referenced Elements show how many items reference them ("This Element is referenced by N item(s).").

Referenced blocks are visually distinguished from local blocks with a dedicated reference colour.

Behind the scenes

  • Reusable Elements are indexed for search and resolved for published output rendering across all block property editors (Block List, Block Grid, Single Block, RTE). New IBlockElementService / BlockElementService resolve shared element content.
  • Block layout items now carry a structural key (distinct from contentKey), and block identity is keyed on the layout key. This lets the same Element be referenced by multiple layout items unambiguously.

Breaking change

UmbBlockLayoutBaseModel now requires a key: string property. Plugin/extension code that constructs block layout objects directly must supply a key. Persisted data without a key is handled on load (a key is derived from the existing contentKey).

How to test

  1. Create or open content using a Block List, Block Grid, Single Block, or RTE-with-blocks property.
  2. Transfer to Library: add a block, fill in its content, then use the block action to Transfer to Library; give it a name. Confirm it appears in the Element Library.
  3. Insert from Library: add another block and choose the Library tab; pick the Element you just created. Confirm it renders as a referenced (coloured) block.
  4. Edit in place: edit the referenced Element's content from the block; confirm the change is reflected on every block that references it.
  5. Disconnect: use Disconnect from Library on one referenced block; confirm it becomes a local copy and the Library Element are unaffected.
  6. Publish/render: publish and view the page on the front-end; confirm referenced block content renders. Confirm reusable Element content is findable via search.
  7. Reference count: confirm the Element reports the correct number of referencing items.

leekelleher and others added 9 commits April 10, 2026 17:39
A block layout item's contentKey can point to either local inline
content or a library element. For shared content, the `isSharedContent`
flag is set to `true`.
Critical:
- disconnectFromLibrary now sets initial expose for new local content
  and cleans up resolved variant state entry
- Extract #updateExposedState() in entry elements, called from all three
  observers (hasExpose, isLibraryElement, sharedContentVariantState) to
  prevent stale unpublished state on library blocks

Important:
- Guard #fetchLibraryElement against already-resolved elements to prevent
  redundant server requests
- Hoist UmbElementDetailRepository to class field in entry elements to
  avoid accumulating dead controllers

Suggestion:
- Fix umb-localize key attributes to use literal keys instead of
  resolved strings from localize.term()
@leekelleher leekelleher changed the title Elements: Reusable block content (WIP) [WIP] Global Elements: Reusable Content of Blocks Apr 13, 2026
kjac and others added 6 commits April 14, 2026 09:15
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts
- Use DocumentVariantStateModel.DRAFT enum instead of magic string
  in both block-list and block-grid entry elements
- Strip isSharedContent from layout during clipboard write to prevent
  pasted blocks from incorrectly appearing as library references
- Guard #setInitialBlockExpose in disconnectFromLibrary against missing
  content type structure
- Store all element variants and resolve against active variantId for
  correct multi-culture state display
- Add already-resolved guard to #fetchLibraryElement
- Add JSDoc on isLibraryElement and sharedContentVariantState observables
- Add .trim() to transfer modal name validation
BREAKING: UmbBlockLayoutBaseModel now requires a `key: string` property.
Plugin code that creates layout objects without `key` will get a compile
error.

- Change UmbArrayState identity functions to use `(x) => x.key`
- Add `layout` setter on entry elements (list, grid, single, rte) that
  extracts both layoutKey and contentKey from the layout object
- Deprecate `contentKey` setter on entry elements (use `layout` instead)
- Add `layoutKey` read-only getter for sorter identity
- Add `setLayoutKey()` / `layoutByKey()` / `getLayoutByKey()` methods
- Update `transferToLibrary` and `disconnectFromLibrary` to take layoutKey
- Update delete operations to find by layout key, only remove shared
  content/settings/exposes if no other layout references the same contentKey
- Migrate grid recursive area operations to use key for identity
- Update `unique` observable on entry context to derive from layout key
- Generate new key on property value clone
- Backwards compat: `setLayouts` assigns `key ??= contentKey` for
  persisted data without key
- Strip `isSharedContent` from clipboard layout clone
- Update sorter configs and repeat key functions
Comment thread src/Umbraco.Web.UI.Client/src/assets/lang/en.ts Outdated
Comment thread src/Umbraco.Web.UI.Client/src/assets/lang/en.ts Outdated
Comment thread src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts Outdated
Comment thread src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entry.context.ts Outdated
Comment thread src/Umbraco.Web.UI.Client/src/packages/block/block/types.ts Outdated
- requestTransferToElementLibrary / requestDisconnectFromElementLibrary → requestTransferToExternalContent / requestDisconnectFromExternalContent (manager + entry context + action callers)
- .addAdditionalPath('library') → 'element'
- #resolvedExternalContent / #resolvedExternalContentVariants → #externalContentValues / #externalContentVariants
- elementStateOf → externalContentStateOf
- hrefObservable / validationDataPathObservable → href / validationDataPath (interface + action impls + default kind element)
- _hasExpose → _localExpose (grid, list, single entry elements)
- BlockWorkspaceHasContentConditionConfig / BlockEntryHasSettingsConditionConfig: type alias → interface
- Remove implementation-specific / AI-ish comments from block-entry, block-manager, action files, block-workspace
- Reuse #elementRepository field in requestTransfer/Disconnect; remove local instantiations
- #fetchExternalContent now accepts an array — one call per layout-state update instead of N
- getHref / getValidationDataPath in edit-content/edit-settings actions now resolve via the observable
@github-actions

Copy link
Copy Markdown

Azure Static Web Apps: Your stage site is ready! Visit it here: https://victorious-ground-017b08103-22448.westeurope.6.azurestaticapps.net

@umbraco umbraco deleted a comment from github-actions Bot Jun 19, 2026
@umbraco umbraco deleted a comment from github-actions Bot Jun 19, 2026
@umbraco umbraco deleted a comment from github-actions Bot Jun 19, 2026
@umbraco umbraco deleted a comment from github-actions Bot Jun 19, 2026
@umbraco umbraco deleted a comment from github-actions Bot Jun 19, 2026
@umbraco umbraco deleted a comment from github-actions Bot Jun 19, 2026
@github-actions

Copy link
Copy Markdown

Azure Static Web Apps: Your stage site is ready! Visit it here: https://orange-sea-0c7411a03-22448.westeurope.6.azurestaticapps.net

@umbraco umbraco deleted a comment from github-actions Bot Jun 19, 2026
@umbraco umbraco deleted a comment from github-actions Bot Jun 19, 2026
@umbraco umbraco deleted a comment from github-actions Bot Jun 19, 2026
@umbraco umbraco deleted a comment from github-actions Bot Jun 19, 2026
@umbraco umbraco deleted a comment from github-actions Bot Jun 19, 2026
@umbraco umbraco deleted a comment from github-actions Bot Jun 19, 2026
@leekelleher
leekelleher merged commit b57867a into v19/dev Jun 23, 2026
28 of 30 checks passed
@leekelleher
leekelleher deleted the v18/feature/reusable-block-content branch June 23, 2026 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/backend area/frontend category/breaking category/notable category/ui User interface category/ux User experience preview/backoffice Pull requests that can be previewed in a static version of the Backoffice preview/storybook Pull requests that can be previewed through Storybook release/19.0.0 type/feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants