{"id":16617329,"url":"https://github.com/navarroaxel/howto-connect-react-custom-element","last_synced_at":"2026-05-10T19:09:44.379Z","repository":{"id":42630102,"uuid":"389428055","full_name":"navarroaxel/howto-connect-react-custom-element","owner":"navarroaxel","description":"How to wrap a react component into a web component","archived":false,"fork":false,"pushed_at":"2023-03-06T06:48:00.000Z","size":897,"stargazers_count":1,"open_issues_count":9,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-02-10T08:32:46.770Z","etag":null,"topics":["custom-elements","howto","howto-tutorial","react","web-component"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/navarroaxel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-25T19:42:03.000Z","updated_at":"2022-10-29T03:46:11.000Z","dependencies_parsed_at":"2025-02-10T08:39:09.938Z","dependency_job_id":null,"html_url":"https://github.com/navarroaxel/howto-connect-react-custom-element","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/navarroaxel%2Fhowto-connect-react-custom-element","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/navarroaxel%2Fhowto-connect-react-custom-element/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/navarroaxel%2Fhowto-connect-react-custom-element/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/navarroaxel%2Fhowto-connect-react-custom-element/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/navarroaxel","download_url":"https://codeload.github.com/navarroaxel/howto-connect-react-custom-element/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247266568,"owners_count":20910837,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["custom-elements","howto","howto-tutorial","react","web-component"],"created_at":"2024-10-12T02:16:14.956Z","updated_at":"2026-05-10T19:09:44.338Z","avatar_url":"https://github.com/navarroaxel.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# howto-connect-react-custom-element\n\nThe React documentation says that React and web components are [complementary to each other](https://reactjs.org/docs/web-components.html). We're going to wrap a React component into a custom element, sending some props as HTML attributes and listen to the `click` event.\n\nI'll assume you know about React and you only want to know how to use custom elements.\n\n## Define a custom element\n\nTo define a web component we should associate a custom tag with a class that wraps the component behavior.\n\n```javascript\nwindow.customElements.define('my-element', MyElement);\n```\n\nThen our class should extend the `HTMLElement` class.\n\n💡 If you want to extend a built-in tag like `p`, you should use the `HTMLParagraphElement` instead.\n\n## The React component\n\nWell, we need a component inside the React world.\n\n```javascript\nconst App = ({text = 'The button wasn\\'t pressed yet.', children, onClick}) =\u003e {\n  const [date] = useState(new Date());\n  return (\n    \u003cdiv\u003e\n      This is a custom element, created at {date.toString()}\n      \u003cbr/\u003e\n      {text}\n      \u003cbr/\u003e\n      \u003cbutton onClick={onClick}\u003eClick me!\u003c/button\u003e\n      \u003cbr/\u003e\n      {children}\n    \u003c/div\u003e\n  );\n};\n```\n\nWe're going to test some React features like `children`, a prop, and a date constant to test if the component is recreated.\n\n## Defining a class for the element\n\nWe should create a `ShadowRoot` for our React component to encapsulate the JavaScript and CSS for this component from the global CSS, this isn't required but it's recommended.\n\nAlso, it's good to separate the constructor from the render of the element itself.\n\n```javascript\nclass MyElement extends HTMLElement {\n  shadow;\n\n  constructor() {\n    // Always call super first in constructor\n    super();\n\n    this.shadow = this.attachShadow({mode: 'open'});\n    // Write element functionality in here\n    this.renderElement();\n  }\n\n  renderElement() {\n    const onClick = this.getAttribute('onclick')\n    const text = this.hasAttribute('text')\n      ? this.getAttribute('text')\n      : undefined;\n    ReactDOM.render(\n      \u003cApp text={text} onClick={onClick}\u003e\n        \u003cslot /\u003e\n      \u003c/App\u003e,\n      this.shadow\n    );\n  }\n\n  // ...\n}\n```\n\nIn the `renderElement` method we take values from the attributes of the HTML tag, like `onclick` and `text`, but what is that wild [`\u003cslot /\u003e`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot) there?\n\nThe `slot` element is a placeholder inside a web component where you can fill your own markup. Sounds similar to [dangerouslySetInnerHTML](https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml). 🙈\n\n💡 You can use several `slot`s in the web component using the `name` attribute.\n\n🧠 If you check the React component's code, the `slot` is placed using the `children` prop.\n\n### The custom element lifecycle\n\nLike the React components, we can define specific methods inside the custom element class to handle the lifecycle, similar to the old fashion class component in React. We're going to see the most important two.\n\n### Unmount a custom element\n\nWe can use `disconnectedCallback` to known when our component is disconnected from the document's DOM.\n\n### Receiving new props form outside\n\nWe should re-render 🙀 our React component if we receive new values for our custom element. We have the `attributeChangedCallback` to let us know when some value changes.\n\nFirst, we should define an array of observable attributes for our component, and then we can re-render the custom element.\n\n```javascript\nclass MyElement extends HTMLElement {\n  static get observedAttributes() {\n    return ['text', 'onclick'];\n  }\n\n  attributeChangedCallback(name, oldValue, newValue) {\n    console.log(`The attribute ${name} was updated.`);\n    this.renderElement();\n  }\n\n  // ...\n}\n```\n\nOk, this looks really easy. 🤔 We take the attribute values each time the `renderElement` is called, so we just need to call it, and the `ReactDOM.render()` API is going to re-render our component and calculate the diffs. 🍰\n\n## Conclusion\n\nNow we can create a modern and light website using just HTML and JavaScript, but plugging in complex UI stuff made with React using the Custom Element interface, or third party React packages if we need one. We are using the best of both worlds. 🎸\n\nHere you have a guide about [Custom Element Best Practices](https://developers.google.com/web/fundamentals/web-components/best-practices) from Google.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnavarroaxel%2Fhowto-connect-react-custom-element","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnavarroaxel%2Fhowto-connect-react-custom-element","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnavarroaxel%2Fhowto-connect-react-custom-element/lists"}