How we implemented a single-page application using React


BuyNowWeb is our latest offering for Train Operating Companies in the UK to sell train tickets. The solution consists of white-labelled web and mobile applications. This post will look into how we implemented a React-based, white-labelled, single-page application.

Nowadays user devices are much more performant. This has made it possible to run application logic on the client side. JavaScript support in modern browsers has come a long way and websites are providing rich user experience comparable with native applications. Because single-page applications can provide a very engaged user experience, we have architected our application to utilise the capabilities of user devices. React became our technology choice because it is a simple and deterministic way of building a user interface composed of small reusable components.

Technology components

  • React-based, white-labelled, single-page application
  • Redux and Redux-router for application state and routes
  • ASP.net web API-based HTTP service

In a single-page application, user flow and application state are maintained in the client (Browser). This reduces server load and at the same time provides rich, engaged user experience. Each white-labelled instance is deployed as an individual IIS website with bundled JavaScript and CSS styles. Individual websites isolate failures and make horizontal scaling easy.

webbrowser-service

Here’s how we implemented different aspects of our solution to support white labelling:

Components and Styles

The user interface is composed of React components. These components are customised via properties (props) which are similar to HTML element attributes.

<Loader type={config.outboundLoaderType}
    visible={showEarlierLaterLoading} />


<Panel id="JourneyPlan"
    title={config.journeyPlanTitle}
    isLocked={false}>
    <JourneyPlan
        label="Outbound"
        journey={outboundJourney} />
    <JourneyPlan
        label="Return"
        journey={inboundJourney}/>
</Panel>

The client side routes are managed by Redux-router. The application state and logic are contained in Redux reducers. They are shared as Node modules and used by web and mobile applications.

action-reducer-store-view

Stylesheets are defined using Sass and are customisable using Sass setting variables. Styles are modularised following BEM so that, when a component is included, it does not impact the style of other components. The settings follow a two-tier approach. The first tier has the global settings such as brand, colours and typography. The second tier is specific to the components (which internally inherit global settings from tier one).

//TOC Name
$toc-name                    : Train Operating Company;
// Color: Brand
$brandColor-primary          : #272460;
$brandColor-secondary        : #1baeb5;
$brandColor-tertiary         : #8397a5;
// Typography: Font Family
$fontFamily-sans-serif       : "Gotham Rounded SSm A", "Gotham Rounded SSm B", Helvetica, Arial, sans-serif;
$fontFamily-base             : $fontFamily-sans-serif;
// Color: Text & Link
$link-color                  : $brandColor-primary;
// Color: Element Active & Background Colors
$component-active-color      : #fff;
$component-active-background : #1baeb5;

Build and Deployment

We have a Rake task to build, test, package and deploy. Each of these tasks invokes different tools specific for each process: so we use npm for managing node modules, nuget for .net dependencies, webpack for JavaScript and CSS bundling and so forth.

Each portal is defined by an Entry.jsx file. This is the entry point for the bundling process. The build task loops over each entry file and invokes webpack by setting up an environment variable (tocName) based on the current portal. The entry file defines the routes and respective React components.

The build task calls webpack with env tocName set to Company/Tenant identifier to pick the respective Sass settings file.

var styleSettingsFilePath = 
        '@import "./node_modules/buynowwebcomponents/StyleSettings/DefaultPrimarySettings.scss";' +
        '@import "./src/web/Content/Styles/TOCSettings/'+tocName+'/PrimarySettings";' +
        '@import "./src/web/Content/Styles/TOCSettings/'+tocName+'/ComponentsSettings/ComponentsSettings";'

With respect to each Entry file, the JavaScript application and CSS style bundle are created by webpack. These along with the required assets are packaged to different portals for each company/brand. This package (.tlpk) is a custom Trainline package which is used by our Trainline cloud deployment tool (Consul) to deploy individual portals over Amazon ASG.

Configuration and fine tuning

Configuration is managed by an HTTP service based on the environment and company brand. Once the React application bootstraps, the configuration is fetched and the values are passed to the React components via props. We build BuyNowWeb features based on user interaction data collected on our existing solution. Fine tuning of features is done using A/B testing. New features undergo user research on prototypes.

Summary

This post explained how we implemented a React-based, single-page application as a white-label solution for UK Train Operating to sell tickets. We found that the single-page application approach provided very engaging user experience and that React met the challenge of building a solution that is both generic and flexible enough to allow companies to reflect their brand.

About the author

Yogesh Sreedharan is a developer with Trainline. He has worked with C#, JavaScript, Java, PHP and Python. Nowadays his interest is around exploring the React ecosystem.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s