Skip to content

feat: MCD-1400 Add an option to pass custom element components only declared props. - #537

Open
drubot wants to merge 2 commits into
2.xfrom
fix/MCD-1400-ce-prop-fallthrough
Open

feat: MCD-1400 Add an option to pass custom element components only declared props.#537
drubot wants to merge 2 commits into
2.xfrom
fix/MCD-1400-ce-prop-fallthrough

Conversation

@drubot

@drubot drubot commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

A custom element is a data payload, but it is handed to the component as vnode
props: every key the component does not declare falls through to its root
element and is serialised as a non-standard HTML attribute. A Drupal-side field
addition then changes the markup of components that never asked for it.

This adds an opt-in way to close that at the CE render boundary:

drupalCe: {
  customElementDeclaredPropsOnly: true
}

With it, a custom element component receives only the props it declares.
Everything else is dropped, and logged in dev mode. Attributes that are valid on
any element still pass through: class, style, id, role, lang, dir,
hidden, tabindex, data-*, aria-* and event listeners — that is how
Drupal's own attributes reach the markup.

Default is false: Vue attribute fallthrough stays the upstream behaviour, so
nothing changes for existing consumers unless they turn it on. The option only
touches elements rendered from custom element JSON; components used from a
template keep normal fallthrough either way.

Why a wrapper

Nuxt registers global components asynchronously, so the declared props are not
readable when the vnode is created. withDeclaredProps() therefore returns an
async component that resolves to a filtering shim around the real one — the
same suspense behaviour as the component it replaces. The wrapper is cached per
resolved component, so a re-render reuses the vnode type instead of remounting
the custom-element subtree.

Note for consumers turning it on

A component that took undeclared keys out of $attrs and relayed them to a
child (v-bind="{ ...$props, ...$attrs }") must declare those props.

Verification

  • test/nuxt/customElementProps.test.ts — with the option off, undeclared keys
    still fall through (the upstream behaviour is asserted); with it on, declared
    props arrive, undeclared keys are dropped for both synchronously and
    asynchronously registered components, global attributes survive, slots still
    render, and a re-render patches in place rather than remounting.
  • Full suite green: 35 files, 163 tests, including the e2e project that
    builds and hydrates real Nuxt apps. eslint . clean apart from one
    pre-existing error in playground/components/global/drupal-view--default.vue.
  • test/nuxt/components/preview.test.ts asserted on the leak itself: its
    fixture sent body as a legacy prop onto node--default.vue, which has a
    body slot, and the assertion passed only because the markup was rendered
    into a body="<p>…</p>" attribute. The fixture now sends it as a slot, so the
    test asserts the rendered body — correct with the option in either position.

Relation to the component-side fix

This closes the CE boundary. It does not cover one kickstart component
v-binding a CE object onto another (v-bind="image" onto LupusImage /
LupusPictureElement), which never crosses that boundary — that is
lupus-nuxt3-kickstart#1401,
where the receiving component sets inheritAttrs: false and binds a filtered
$attrs. Between the two, both classes are closed.

Ticket: MCD-1400

fago added 2 commits August 28, 2026 18:33
Vue attribute fallthrough for custom elements stays the default; the
filtering is enabled with `customElementDeclaredPropsOnly`.
@drubot drubot changed the title fix: MCD-1400 Only pass custom element components the props they declare. feat: MCD-1400 Add an option to pass custom element components only declared props. Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants