Using React in Vanilla Web Components

Davis Hammon
2 min readSep 27, 2018

React is powerful and really has taken the web developer community by storm. And for good reason. Its fast, modular, data driven approach is fantastic.

But the web is always on the move. And I’m really excited as Web Components mature. Firefox is now supporting the Shadow DOM (albeit behind a flag). And Edge isn’t far behind. And we have https://www.webcomponents.org/polyfills/ today, to start using the technology now.

What I want to explore is leveraging one’s existing React components within a vanilla Web Components or lit-html / Polymer environment. I wrote a gulp plugin that will take a react file and write in a thin custom element wrapper.

This is a react file before the conversion:

import { Component } from 'react'class MyComponent extends Component {
constructor(props) {
super(props)
this.methodA = this.methodA.bind(this)

this.state = { mydata: 'initial state' }
}

methodA() {
this.setState((prevState, props) => {
return {...prevState, mydata: 'updated state'}
})
}
componentDidMount() {
//
}
render() {
return (
<div onClick={this.methodA}>
<h1>{this.state.mydata}</h1>
<section>
content
</section>
</div>
)
}
}
export default MyReact

and, after:

class ReactMyComponent extends React.Component {
constructor(props) {
super(props);
this.methodA = this.methodA.bind(this);
this.state = { mydata: 'initial state' };
}
methodA() {
this.setState((prevState, props) => {
return {...prevState, mydata: 'updated state'}
})
}
componentDidMount() {
//
}
render() {
return React.createElement("div", {
onClick: this.methodA
}, React.createElement("h1", {
ref: "someref"
}, this.state.mydata), React.createElement("section", null, "content"));
}
}// *******
// write a new class (named the same) to wrap the React
// *******
class MyComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
}
connectedCallback() {
ReactDOM.render(
React.createElement(ReactMyComponent, {}, null),
this.shadowRoot
);
}
}
window.customElements.define('mycomponent-react', MyReact);

As you can see, it creates a new class at the bottom of the file (named as the original) and runs window.customElements to define the new component. Inside the connectedCallback, it hooks into ReactDOM to render out the react class component.

Keep in mind that you will need to include the React and ReactDOM libraries as imports within your index.html file (or main html). Like so:

<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

Usage within your gulp build would look something like this:

First, install the plugin like this:

npm i gulp-react-to-vanilla-component --save-dev

Then, inside of your gulp file:

const reactToVanilla = require('gulp-react-to-vanilla-component');function _compileJS(glob) {
return gulp.src(glob, {base: '.'})
.pipe(babel({
plugins: [
'babel-plugin-transform-import-resolve',
'babel-plugin-transform-react-jsx'
]
}))
.pipe(reactToVanilla('Component', 'react'))
.pipe(gulp.dest('build'))
}

Run gulp and you should get an output file that is ready to be used. You can use it in your web components file like this (using lit-html for data binding):

import '/components/myreact/myreact.js';
import { html, render } from '/node_modules/lit-html/lit-html.js'
class CustomElement extends HTMLElement { constructor() {
super();
this.props = {};
this.attachShadow({mode: 'open'}); this.renderer = () => html`
<div>
<p>A Custom Element</p>
<mycomponent-react></mycomponent-react>
</div>
`;
},
connectedCallback() {
this.render()
},
render () {
render(this.renderer(this), this.shadowRoot)
}
}
window.customElements.define('view-default', CustomElement);

Thats pretty much it. As we increasingly move toward Web Components hopefully this helps in making the transition easier!

--

--