Skip to content

Instantly share code, notes, and snippets.

@developit
Last active November 5, 2023 11:16
Show Gist options
  • Star 33 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save developit/35183bb5a0b37ddcd375a89f67bae816 to your computer and use it in GitHub Desktop.
Save developit/35183bb5a0b37ddcd375a89f67bae816 to your computer and use it in GitHub Desktop.
Preact + Web Components = <333 Demo: http://www.webpackbin.com/VJyU9wK5W
import { h, Component } from 'preact';
import Markup from 'preact-markup';
import register from './preact-custom-element';
// just a proxy component: WC -> Preact -> WC
const A = () => <x-b foo="initial foo from <x-a>" />;
// stateful component that can re-render
class B extends Component {
render(props, state) {
return (
<div>
<label style={{ display:'block' }}>
foo=
<input onInput={this.linkState('foo')} />
</label>
<x-c foo={state.foo || props.foo} />
</div>
);
}
}
// accepts props (as attributes) from B (<x-b>)
const C = ({ foo }) => (
<span>I am {'<x-c>'}. I got passed foo = <code>{foo}</code></span>
);
// register stuff (or do it inline above)
register(A, 'x-a');
register(B, 'x-b');
register(C, 'x-c');
// WC on the outside:
document.body.innerHTML = '<x-a></x-a>';
import { h, render } from 'preact';
const Empty = () => null;
export default function register(Component, tagName) {
let prototype = Object.create(HTMLElement.prototype);
prototype._vdomComponent = Component;
prototype.attachedCallback = prototype.attributeChangedCallback = renderElement;
prototype.detachedCallback = unRenderElement;
return document.registerElement(
tagName || Component.displayName || Component.name,
{ prototype }
);
}
function renderElement() {
this._root = render(
toVdom(this, this._vdomComponent),
this.shadowRoot || this.createShadowRoot(),
this._root
);
}
function unRenderElement() {
render(h(Empty), this.shadowRoot, this._root);
}
function toVdom(element, nodeName) {
if (element.nodeType===3) return element.nodeValue;
if (element.nodeType!==1) return null;
let children=[], props={}, i=0, a=element.attributes, cn=element.childNodes;
for (i=a.length; i--; ) props[a[i].name] = a[i].value;
for (i=cn.length; i--; ) children[i] = toVdom(cn[i]);
return h(nodeName || element.nodeName.toLowerCase(), props, children);
}
@mhadaily
Copy link

Jason, Example link above doesn't work anymore, I have create a working example now https://codesandbox.io/s/preact-web-components-z2g9e

@ReneCode
Copy link

@mhadaily thank you very much - your correction helped me a lot.

@mhadaily
Copy link

@RendeCode, perfect, I am glad but keep in mind this example will not work anymore at all because of https://developer.mozilla.org/en-US/docs/Web/API/Document/registerElement (This API was removed from browsers) and you should follow this https://preactjs.com/guide/v10/web-components/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment