Skip to content

Instantly share code, notes, and snippets.

@jonathantneal
Last active July 20, 2021 23:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonathantneal/b3df57a4c59cd70e0d18437352586794 to your computer and use it in GitHub Desktop.
Save jonathantneal/b3df57a4c59cd70e0d18437352586794 to your computer and use it in GitHub Desktop.
React Custom Elements

React Custom Elements

Use Web Components as React Components.

Usage

Import reactCustomElements as a module:

import * as reactCustomElements from 'https://gist.githack.com/jonathantneal/b3df57a4c59cd70e0d18437352586794/raw/reactCustomElements.esm.min.js'

const Shout = reactCustomElements.define('h-shout', class extends HTMLElement {
  constructor() {
    super().attachShadow({ mode: 'open' }).innerHTML = `<slot></slot>!`
  }
})

ReactDOM.render(
  React.createElement(Shout, {}, 'Hello')
)

Alternatively, import reactCustomElements as a self-defined variable:

<script src="https://gist.githack.com/jonathantneal/b3df57a4c59cd70e0d18437352586794/raw/reactCustomElements.self.min.js"></script>
<script>
const Shout = reactCustomElements.define('h-shout', class extends HTMLElement {
  constructor() {
    super().attachShadow({ mode: 'open' }).innerHTML = `<slot></slot>!`
  }
})

ReactDOM.render(
  React.createElement(Shout, {}, 'Hello')
)
</script>

reactCustomElements.esm.min.js is 531 bytes, 337 bytes gzipped.

reactCustomElements.self.min.js is 545 bytes, 340 bytes gzipped.

/** Returns a React Component from a Custom Element class. */
export type ReactCustomElementComponent<T extends CustomElementConstructor> = React.ForwardRefExoticComponent<
& Partial<Omit<T extends new (...args: any[]) => infer E ? E : never, keyof HTMLElement>>
& React.HTMLAttributes<HTMLElement>
>
export interface ReactCustomElementRegistry {
/** Returns a React Component that renders a Custom Element. */
define <T extends CustomElementConstructor>(name: string, CustomElement?: T): ReactCustomElementComponent<T>
}
export declare const define: ReactCustomElementRegistry['define']
let { ownKeys } = Reflect
let { create, getPrototypeOf } = Object
let { createElement, forwardRef } = React
let registry = create(null)
/** Returns a React Component that renders a Custom Element. */
export let define = (name, CustomElement) => {
if (name in registry) return registry[name]
/** Properties of the Custom Element. */
let dom = create(null)
// calculate possible properties of the Custom Element
for (
let { prototype } = customElements.get(name) || customElements.define(name, CustomElement) || CustomElement;
prototype instanceof Node;
prototype = getPrototypeOf(prototype)
) for (let k of ownKeys(prototype)) dom[k] = 1
// omit special React properties
delete dom.children
delete dom.ref
delete dom.style
/** React Component returning the Custom Element. */
let Component = registry[name] = forwardRef((props, ref) => {
/** Properties to add to the Custom Element. */
let kDOM = {}
/** Attributes to add to the Custom Element. */
let kJSX = {
ref(current) {
if (current) for (let k in kDOM) current[k] = kDOM[k]
if (typeof ref === 'function') ref(current)
else if (ref) ref.current = current
}
}
// push props to either properties or attributes
for (let k in props) (k in dom ? kDOM : kJSX)[k] = props[k]
// return the created Custom Element
return createElement(name, kJSX)
})
// assign the React Component display name as the Custom Element name
Component.displayName = name
// return the React Component
return Component
}
let{ownKeys:e}=Reflect,{create:t,getPrototypeOf:l}=Object,{createElement:r,forwardRef:n}=React,f=t(null);export let define=(o,i)=>{if(o in f)return f[o];let c=t(null);for(let{prototype:t}=customElements.get(o)||customElements.define(o,i)||i;t instanceof Node;t=l(t))for(let l of e(t))c[l]=1;delete c.children,delete c.ref,delete c.style;let d=f[o]=n(((e,t)=>{let l={},n={ref(e){if(e)for(let t in l)e[t]=l[t];"function"==typeof t?t(e):t&&(t.current=e)}};for(let t in e)(t in c?l:n)[t]=e[t];return r(o,n)}));return d.displayName=o,d}
reactCustomElements = ((
{ ownKeys } = Reflect,
{ create, getPrototypeOf } = Object,
{ createElement, forwardRef } = React,
registry = create(null),
) => ({
/** Returns a React Component that renders a Custom Element. */
define(name, CustomElement) {
if (name in registry) return registry[name]
/** Properties of the Custom Element. */
let dom = create(null)
// calculate possible properties of the Custom Element
for (
let { prototype } = customElements.get(name) || customElements.define(name, CustomElement) || CustomElement;
prototype instanceof Node;
prototype = getPrototypeOf(prototype)
) for (let k of ownKeys(prototype)) dom[k] = 1
// omit special React properties
delete dom.children
delete dom.ref
delete dom.style
/** React Component returning the Custom Element. */
let Component = registry[name] = forwardRef((props, ref) => {
/** Properties to add to the Custom Element. */
let kDOM = {}
/** Attributes to add to the Custom Element. */
let kJSX = {
ref(current) {
if (current) for (let k in kDOM) current[k] = kDOM[k]
if (typeof ref === 'function') ref(current)
else if (ref) ref.current = current
}
}
// push props to either properties or attributes
for (let k in props) (k in dom ? kDOM : kJSX)[k] = props[k]
// return the created Custom Element
return createElement(name, kJSX)
})
// assign the React Component display name as the Custom Element name
Component.displayName = name
// return the React Component
return Component
}
}))()
reactCustomElements=(({ownKeys:e}=Reflect,{create:t,getPrototypeOf:l}=Object,{createElement:n,forwardRef:r}=React,f=t(null))=>({define(o,c){if(o in f)return f[o];let i=t(null);for(let{prototype:t}=customElements.get(o)||customElements.define(o,c)||c;t instanceof Node;t=l(t))for(let l of e(t))i[l]=1;delete i.children,delete i.ref,delete i.style;let s=f[o]=r(((e,t)=>{let l={},r={ref(e){if(e)for(let t in l)e[t]=l[t];"function"==typeof t?t(e):t&&(t.current=e)}};for(let t in e)(t in i?l:r)[t]=e[t];return n(o,r)}));return s.displayName=o,s}}))()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment