From fff10adb1e03d8a717f7dc878c6c1974d4099a4e Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Fri, 22 Aug 2025 04:34:39 -0700 Subject: [PATCH 1/3] Add more information about event and property binding on custom elements --- .../reference/react-dom/components/index.md | 94 +++++++++++++++++-- 1 file changed, 84 insertions(+), 10 deletions(-) diff --git a/src/content/reference/react-dom/components/index.md b/src/content/reference/react-dom/components/index.md index ec2e1d2eea1..1c811995dbd 100644 --- a/src/content/reference/react-dom/components/index.md +++ b/src/content/reference/react-dom/components/index.md @@ -162,23 +162,97 @@ Similar to the [DOM standard,](https://developer.mozilla.org/en-US/docs/Web/API/ ### Custom HTML elements {/*custom-html-elements*/} -If you render a tag with a dash, like ``, React will assume you want to render a [custom HTML element.](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) In React, rendering custom elements works differently from rendering built-in browser tags: - -- All custom element props are serialized to strings and are always set using attributes. -- Custom elements accept `class` rather than `className`, and `for` rather than `htmlFor`. +If you render a tag with a dash, like ``, React will assume you want to render a [custom HTML element.](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) If you render a built-in browser HTML element with an [`is`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/is) attribute, it will also be treated as a custom element. - +#### Setting values on custom elements {/*attributes-vs-properties*/} + +Custom elements have two methods of passing data into them: + +1) Attributes: Which are displayed in markup and can only be set to string values +2) Properties: Which are not displayed in markup and can be set to arbitrary JavaScript values + +By default, React will pass values bound in JSX as attributes: + +```jsx + +``` + +Non-string JavaScript values passed to custom elements will be serialized by default: + +```jsx +// Will be passed as `"1,2,3"` as the output of `[1,2,3].toString()` + +``` + +React will, however, recognize an custom element's property as one that it may pass arbitrary values to if the property name shows up on the class during construction: + +```javascript {4-6} +class MyElement extends HTMLElement { + constructor() { + super(); + // The value here will be overwritten by React + // when initialized as an element + this.value = undefined; + } -[A future version of React will include more comprehensive support for custom elements.](https://github.com/facebook/react/issues/11347#issuecomment-1122275286) + connectedCallback() { + this.innerHTML = this.value.join(", "); + } +} +``` -You can try it by upgrading React packages to the most recent experimental version: +#### Listening for events on custom elements {/*custom-element-events*/} + +A common pattern when using custom elements is that they may dispatch [`CustomEvent`s](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) rather than accept a function to call when an event occur: + +```javascript {8-15} +class MyElement extends HTMLElement { + constructor() { + super(); + this.test = undefined; + this.emitEvent = this._emitEvent.bind(this); + } + + _emitEvent() { + const event = new CustomEvent('speak', { + detail: { + message: 'Hello, world!', + }, + }); + this.dispatchEvent(event); + } + + connectedCallback() { + this.el = document.createElement('button'); + this.el.innerText = 'Say hi'; + this.el.addEventListener('click', this.emitEvent); + this.appendChild(this.el); + } + + disconnectedCallback() { + this.el.removeEventListener('click', this.emitEvent); + } +} +``` + +You can listen for these events using an `on` prefix when binding to the event via JSX: + +```jsx + console.log(e.detail.message)}> +``` + + -- `react@experimental` -- `react-dom@experimental` +Events are case-sensative and support dashes (`-`). Preserve the casing of the event and include all dashes when listening for custom element's events: -Experimental versions of React may contain bugs. Don't use them in production. +```jsx +// Listens for `say-hi` events + +// Listens for `sayHi` events + +``` --- From 748fb42952f7515360d962e4fdd732331f86f72a Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Fri, 22 Aug 2025 07:36:28 -0700 Subject: [PATCH 2/3] docs: migrate code samples to Sandpack --- .../reference/react-dom/components/index.md | 58 ++++++++++++++++--- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/src/content/reference/react-dom/components/index.md b/src/content/reference/react-dom/components/index.md index 1c811995dbd..36c88bc2e40 100644 --- a/src/content/reference/react-dom/components/index.md +++ b/src/content/reference/react-dom/components/index.md @@ -188,8 +188,21 @@ Non-string JavaScript values passed to custom elements will be serialized by def React will, however, recognize an custom element's property as one that it may pass arbitrary values to if the property name shows up on the class during construction: -```javascript {4-6} -class MyElement extends HTMLElement { + + +```js src/index.js hidden +import {MyElement} from './MyElement.js'; +import { createRoot } from 'react-dom/client'; +import {App} from "./App.js"; + +customElements.define('my-element', MyElement); + +const root = createRoot(document.getElementById('root')) +root.render(); +``` + +```js src/MyElement.js active +export class MyElement extends HTMLElement { constructor() { super(); // The value here will be overwritten by React @@ -203,12 +216,33 @@ class MyElement extends HTMLElement { } ``` +```js src/App.js +export function App() { + return +} +``` + + + #### Listening for events on custom elements {/*custom-element-events*/} -A common pattern when using custom elements is that they may dispatch [`CustomEvent`s](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) rather than accept a function to call when an event occur: +A common pattern when using custom elements is that they may dispatch [`CustomEvent`s](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) rather than accept a function to call when an event occur. You can listen for these events using an `on` prefix when binding to the event via JSX. -```javascript {8-15} -class MyElement extends HTMLElement { + + +```js src/index.js hidden +import {MyElement} from './MyElement.js'; +import { createRoot } from 'react-dom/client'; +import {App} from "./App.js"; + +customElements.define('my-element', MyElement); + +const root = createRoot(document.getElementById('root')) +root.render(); +``` + +```javascript src/MyElement.js +export class MyElement extends HTMLElement { constructor() { super(); this.test = undefined; @@ -237,12 +271,18 @@ class MyElement extends HTMLElement { } ``` -You can listen for these events using an `on` prefix when binding to the event via JSX: - -```jsx - console.log(e.detail.message)}> +```jsx src/App.js active +export function App() { + return ( + console.log(e.detail.message)} + > + ) +} ``` + + Events are case-sensative and support dashes (`-`). Preserve the casing of the event and include all dashes when listening for custom element's events: From 7e893fdadbaf9bbaba6ef19ab9654f6d370248cf Mon Sep 17 00:00:00 2001 From: "Sebastian \"Sebbie\" Silbermann" Date: Fri, 22 Aug 2025 18:08:39 +0200 Subject: [PATCH 3/3] Update src/content/reference/react-dom/components/index.md --- src/content/reference/react-dom/components/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/reference/react-dom/components/index.md b/src/content/reference/react-dom/components/index.md index 36c88bc2e40..586663398c6 100644 --- a/src/content/reference/react-dom/components/index.md +++ b/src/content/reference/react-dom/components/index.md @@ -285,7 +285,7 @@ export function App() { -Events are case-sensative and support dashes (`-`). Preserve the casing of the event and include all dashes when listening for custom element's events: +Events are case-sensitive and support dashes (`-`). Preserve the casing of the event and include all dashes when listening for custom element's events: ```jsx // Listens for `say-hi` events