Writing JSX to build pixel perfect UI elements can be very time consuming and frustrating. I’m talking about the process of figuring out the exact padding, margin, flexbox, width, height etc to match the required design spec. As React engineers, our time is most well spent on what we can do uniquely - write state management logic, API calls, error handling, component architecture, animations and building complex components that require lots of user interactions.
The aim of this blog post is to provide a methodology using which designers can build the simpler UI HTML elements using Webflow and React engineers can inject complex elements / React components into the generated HTML page. This works best for sites that have less user specific / user generated content - like several b2b sites.
Note: For those who do not know about Webflow, it’s a tool that let’s designers design the UI, and it emits fully functional HTML code.
Technical guide
As an example, let’s consider a webpage where most of the body is generated using Webflow apart from the header and the footer - which are arbitrarily complex react components.
Creating empty divs
We start by assuming that we have already generated the HTML, JS and CSS files using webflow. The HTML file contains the head and body tags as usual.
We want to insert our React header
component at the top of the body, so we add an empty <div>
element with a unique id:
<head>...</head>
<body>
<div id="webflow-header"></div>
... HTML generated by webflow
</body>
As we will see later, the header
component will be rendered inside the div above. We will get the ref of the div using the id “webflow-header”
We then want to make place for the footer, which should go right before the closing body tag:
<head>...</head>
<body>
<div id="webflow-header"></div>
... HTML generated by webflow
<div id="webflow-footer"></div>
</body>
We also want to create a div where the whole react app will load in. Think of this as an entrypoint for the react app. From here, it will render the header
and footer
component into the two divs created above. We will do this using the createPortal function provided by React (as we will see later):
<head>...</head>
<body>
<div id="webflow-header"></div>
... HTML generated by webflow
<div id="webflow-footer"></div>
<div id="react-root"></div>
</body>
Adding the React bundle
Just like in a normal react
app, we need to add the <script>
tag that will load the bundle and run the React app:
<script src = "bundle.js"></script >
Loading the React app
We want to load the react app in the div with the ID “react-root”
. This will load the root
component of your react app, which can then load the header
and footer
react component in their respective div tags. The react app can also be used for any other operation like analytics:
ReactDOM.render((
<Router />
), document.getElementById("react-root"));
Find the header and footer div nodes from the React app
In order to inject the header
and footer
components into their divs, we need to first get a reference to them. We do so using the document.getElementById
function in the root
component of the react app
this.headerDiv = document.getElementById("webflow-header");
this.footerDiv = document.getElementById("webflow-footer");
In pages where the divs have not been inserted, the value for this.headerDiv
and this.footerDiv
will be null. So we need to remember to do a null check before using them.
The render function
In order to inject the components, we have to call the createPortal
function from inside the render
function. This function takes two arguments:
- The react component to render
- The DOM node reference (in our example, it’s
this.headerDiv
orthis.footerDiv
)
render() {
let result = [];
if (this.headerDiv) {
result.push(ReactDOM.createPortal(<Header />, this.headerDiv));
}
if (this.footerDiv) {
result.push(ReactDOM.createPortal(<Footer />, this.footerDiv))
}
return result;
}
- Notice that we check that
footerDiv
andheaderDiv
are not falsy value - which will be the case when the div elements are not added into the webflow html page. - We create an array of JSX elements to inject more than one react component. If the number of components to inject is just one, then we can simply do return
ReactDOM.createPortal(<Header />, this.headerDiv)
We have successfully added react components to a webflow generated HTML page.
However…
Since the injection of the components happen after the react bundle is downloaded and run, the user first sees the HTML that’s generated using Webflow, and then sees a jitter as the react components are injected (which occupy their own height and width). Unfortunately, this jitter is quite noticeable.
We can fix this by setting the body tag’s CSS to have display: none
initially, and then after the components have been injected, we can set the display to block (in the componentDidMount
or useEffect
function). This way, the experience for the user is the same as if the whole page is made using React.
So in the componentDidMount
/ useEffect
function, we should do the following
let body = document.getElementsByTagName("body")[0];
if (body !== null) {
body.style.display = "block";
}
The above would prevent the jitter and provide a seamless experience for the user.
In Conclusion
We saw how we can inject components into an existing HTML page generated via Webflow using the ReactDOM.createPortal
function.
This method allows for quicker development of relatively static web pages saving you money. As a reference, we at SuperTokens use this method. Our home page is made completely using Webflow, excepting for the header and footer - which are injected using the method shown above.