At UNRVLD, we build solutions on a number of DXP platforms, including the Optimizely Content and Commerce platforms.
We have a significant number of clients using Optimizely Content up to v11 and we've been considering how we might be able to deliver some of the more complex and heavily interactive user journeys our clients need by leveraging modern front-end frameworks. In this post, I want to walk you through how we've recently architected the introduction of React to part of the site for one of our largest e-commerce partners.
This is the first part of a series looking at how we approached this challenge as an organization in which I'll summarize the technologies and solutions. In subsequent parts, I'll break down some of the key aspects in more technical detail - keep an eye out for these over the coming weeks.
What's the motivation?
Front-end frameworks are obviously becoming huge in terms of developing web solutions and user interfaces. With Optimizely Cloud being built on a server-based ASP.NET MVC architecture, there's a challenge to leveraging some JavaScript tools.
But user expectations are growing ever higher. Performance, personalization and quality experiences are essential for any site and significant improvements in these areas can have huge impact on conversions in an e-commerce context. As an agency, we have the design and UX expertise to know what works in these areas and this exercise was about how to implement those ideas technically.
What would we need?
So how would we inject a React app with all its dependencies on CMS content and configuration into the existing site and have it render on-screen?
We would need:
- The React application itself
- A placeholder within the Razor views of the MVC site in which to inject the React code
- A mechanism to provide the required data to the React app
- Support for server-side rendering (SSR)
I'll go into the thought processes (e.g., why React?) and structure of these individual items in further posts throughout this series.
The React App
Items 1 and 2 were intrinsically tied. We had to have a React app and then confirm that we could get it to render in the context of using Razor views. On the surface, this seems pretty trivial - you just need some JS files declared as a bundle and included in your Razor! But this was key to deciding if the approach would work long-term, so we had to prove it as a concept.
We also had to decide whether or not to use TypeScript. With 20+ years of writing JavaScript under my belt, I've been a slightly hesitant adopter of TypeScript, but it honestly feels like starting any significant JS development project nowadays without it is churlish. TypeScript it was - more on that in a later post.
We created a very simple app initially to ensure it could be embedded into the site as we wanted. Since "create-react-app" generally focuses on SPAs and includes a lot of features we didn't expect to need, we decided against it but instead hand-crafted the packages and files we needed.
For context, our first index.tsx
app looked something like this:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';
ReactDOM.render(
<App />,
document.getElementById('react-app');
)
Using Webpack, we built this to an existing folder within the .NET web application to make it easily referenceable using the following partial config:
module.exports = (env, argv) => {
return {
entry: {
'react-app': './src/index.tsx'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, '../static')
},
resolve: {
extensions: ['.ts', '.tsx', '.js']
}
}
}
We quickly reached a point of having a compiled JavaScript file that we could reference in our Optimizely Content site.
Razor Placeholder
The next task was to add something to the Razor views to get the app to appear on screen, leading us to this:
<div id="react-app"></div>
@Html.VersionedJs("~/static/react-app.js")
With a quick npm run build
of our React app and a spin-up of the Optimizely MVC site, we browsed to the necessary address. Et voilà! A React app rendered inside an Optimizely view.
Injecting Data (Populating State)
The most important element of making this work was ensuring that the React app had the required data. This would obviously be built and prepared by the back-end C#, but how best to get it into the app? Really, it comes down to one of two options:
- Render the data in some way on the page (e.g. hidden field) and consume it within your React start-up.
- Mount the React app and trigger an immediate callback to fetch the data.
The trade offs here are about performance and user perception of speed. We went for the former initially, purely because a lot of the work to build the data was done on page load behind the scenes, so it didn't really make sense to get a blank, placeholder UI on screen before then re-requesting much of the already built data. We may review that over time as our app grows.
Using Redux for state management (which will be important in the SSR section below), our app instantiation code expanded to look like this:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './app';
import { initStore } from './redux/store';
const dataBlob = (document.getElementById('initial-state') as HTMLElement).value;
const storeData = JSON.parse(dataBlob);
const store = initStore(storeData);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
);
Server Side Rendering
Given this React app was being included in a major e-commerce solution with significant SEO foundations already in place, it became clear we would need to implement server-side rendering (SSR) of our React app to maintain those high scores and visibility. This was definitely one of the more interesting aspects of the overall project and will be covered in detail in a later post - it turned into a very expansive aspect, too!
Summary
So, that's the overview - a proof of concept idea to include a significant, standalone React element in a long-established ASP.NET MVC application, with state management and SSR to support continued SEO efforts, taken through to a production-quality implementation.
In the rest of the series, I'll go into more detail about the different parts of the solution and hopefully be able to share something of how the changes have been received by our client and their customers once released.
Top comments (0)