A library to render React components in a Custom Element. Please note that this library supports V1 of the custom elements spec only.
This library enables us to migrate by rendering custom components with server side templating, that are then backed by React components in the browser. We can incrementally expand the React components and reduce the work done by server side rendering.
PS: if you like this, you might like elm-web-components
.
We previously used ReactiveElements
, but that project's goal is to very strictly adhere to the web component spec (for example, by supporting <slot>
s). Our goal is to use custom elements as a thin wrapper to help us migrate, so custom-react-elements
does not attempt to support the entire web component spectrum. A lot of credit for the code within this repository must go to the authors and contributors of ReactiveElements
.
For yarn
:
yarn add @teamthread/custom-react-elements
And for npm
:
npm install @teamthread/custom-react-elements
You can find a full example in the example
folder of this repository if you'd like to dive further into the library.
import React from 'react';
import CustomReactElements from '@teamthread/custom-react-elements';
const MyComponent = () => <p>Hello, world!</p>;
CustomReactElements.define('my-component', MyComponent);
You can then use this element in an HTML file:
<my-component></my-component>
You can pass attributes into a custom element that make it through to React as a prop. Any attributes with hyphens will be camelCased:
<my-component name="Jack"></my-component>
<!-- React will see this.props.name === "Jack" -->
You can also pass the literal strings "true"
or "false"
and they will be converted to booleans in React land.
<my-component logged-in="true"></my-component>
<!-- React will see this.props.loggedIn === true -->
<!-- note this is the boolean `true`, NOT a string! -->
If you wish to disable this feature, you can pass an option when you define the component:
CustomReactElements.define('my-component', MyComponent, {
disableBooleanTransforms: true,
});
Now, the strings "true"
and "false"
will be passed through as is.
Additionally, you can pass JSON arrays or objects. These have to be surrounded with an extra pair of braces:
<my-component names='{["alice", "bob"]}'></my-component>
<!-- React will see this.props.names = ["alice", "bob"] -->
<my-component person='{{"name": "alice"}}'></my-component>
<!-- React will see this.props.person = { name: 'alice' } -->
When a React component is mounted from a custom element, it is given two additional props:
props.children
: this is aDocumentFragment
of the children within a component:<my-component><p>foo</p></my-component>
props.container
: this is anHTMLElement
containing the wrapper element.
In our experience you should try to avoid using these; we like to use custom elements with no children and let React do the rest.
A custom element that has an attribute changed will cause React to update. So if you have some other code that's swapping out attributes, React will act accordingly. Again, we recommend avoiding this if possible and having all logic within React whenever possible.
By default, the custom elements created by this library will not use the ShadowDOM. We do this because turning on the ShadowDOM stops a component inheriting CSS styles from your global styles. You can turn the ShadowDOM on when defining the component:
CustomReactElements.define('my-component', MyComponent, {
useShadowDom: true,
});
Old browsers that don't support the v1 custom elements spec (IE11, for example), can have support provided from the excellent document-register-element
polyfill. The best way to do this is to include these scripts in your HTML files:
<script>this.customElements||document.write('<script src="//unpkg.com/document-register-element"><\x2fscript>');</script>
<script src="//unpkg.com/built-in-element"></script>
You can check caniuse.com for the latest information on browsers that support the V1 spec.
We'd welcome any contributions to this library! Please feel free to open an issue if you find any bugs, or would like thoughts on any ideas for improvements that you have.
To run the repository locally, you should:
- clone this repository (or fork it first if you're planning to contribute)
- run
yarn install
to ensure you've got all dependencies set up - You can run
yarn run dev-test
to run the tests locally via Karma.yarn run lint
will run ESLint. - Running
yarn run example
will run a small demo project that you can play with in the browser.
For any new features, we ask that you provide at least one test that covers the functionality. If you're unsure or need any help, please ask :)