Based on our implementation, we have created a starter kit for anyone who wishes to learn or implement Adaptive Web Design in their application.

Here is a rough outline of what we will be doing to implement Adaptive Web Design in a React-Redux application.

Create an App Shell Create Page Components for Desktop and Mobile Devices Create an Express Server with feature to detect the device type Create a shared Redux store between the Client and the Server Set the device type in the store and pass it down to the browser Use the value in the store to conditionally render the components

Note: We will not be going through the details of the webpack and other configurations of the project.

We will be going through the above steps one by one with an example in the section below.

Project Structure

Our project will follow the folder structure as shown below.

Client and Server logic are kept in separate folders.

In the client folder, we have an App Shell file (App.jsx) which holds the routing and shell logic. The pages folder will have all the page templates of different devices identified by the file extension.

We have designed the app in such a way that the actions, reducers, and services for the devices are kept the same. We will see that structure further down the article.

Create an App Shell

First, we create an App shell which will be common for desktop and mobile templates. You can have different App Shells for different device types. We keep it the same for simplicity.

We add the following code in the App.jsx file for creating the App Shell.

App Shell (common for desktop and mobile)

In the App Shell, we use styled-components to create a header which will be common for desktop and mobile.

Create Page Components

After creating the App Shell, we create two files in the pages folder, `home.desktop.jsx` and `home.mobile.jsx` which are the page files of the Home page. We have the files segregated with the device name as the extension.

Let’s create a simple page component that displays a line of text along with the device type.

Desktop Component

Desktop Page Component (home.desktop.js)

Mobile Component

Mobile Page Component (home.mobile.js)

In the above files, we created two simple page components. One for Desktop and One for Mobile.

Create an Express Server

We use express as our web server which will receive the first request and send the HTML response back to the browser.

In our case, only the first request will be served by Express, all the other navigation will be handled on the client-side by React.

In the server folder, we create the file `index.js` with the following code.

Express Server (index.js)

Once we have the server up and running, we use express-device package to find the device type.

const device = require(‘express-device’);

app.use(device.capture());

Now if you try to print req.device in the console, you will see the device object containing the following data.

{ parser:

DeviceParser {

options:

{ emptyUserAgentDeviceType: 'desktop',

unknownUserAgentDeviceType: 'phone',

botUserAgentDeviceType: 'bot',

carUserAgentDeviceType: 'car',

consoleUserAgentDeviceType: 'tv',

tvUserAgentDeviceType: 'tv',

parseUserAgent: false

},

make_sure_parser_was_executed: [Function],

get_model: [Function],

get_type: [Function]

},

type: 'desktop',

name: ''

}

As you can see, we can get the type of device from req.device

Create Redux Store

The next step is to create a redux store for storing the device type and other data as necessary.

We create a reducer file called core.reducer.js and set the initialState as mentioned below and export it for combining it with other reducers in the index reducer file.

const initialState = {

config: {

deviceType: ''

}

};

In the index reducer file, we import the core reducer and combine it using combineReducer.

Note: In the example, we have only one reducer to explain the concept.

Once the reducer is set up, we create the store using a function that will be shared by both the client and the server.

configureStore method (store.js)

In the client folder, we import the configureStore function in the index.js file to create the store and add it to the Provider. We do the same in the server part as well.

Client Store Creation

import { configureStore } from '../server/framework/store';

const initialState = {};

const store = configureStore(initialState);

Server Store Creation

import { configureStore } from './store';

const store = configureStore({});

Set the device type on the Server State and pass to Client

Once the store is created in the server, we extract that state using the getState() function.

let state = store.getState();

After that, we set the device type which we got from req.device.type in the state object.

state.core.config.deviceType = req.device.type;

Now, we have the device type stored in the state object which can be passed down to the client by setting it in the WINDOW object.

To do this, we take the HTML text which we got from reading the main.html file. We append the stringified state object to the end of the HTML and pass it on to the client as response.

const store = configureStore({});

const state = store.getState();

state.core.config.deviceType = req.device.type;

const htmlPath = path.resolve(__dirname, '../../build/main.html');

fs.readFile(htmlPath, 'utf8', (err, data) => {

const html = data.replace('<script></script>',

`<script>window.__PRELOADED_STATE__=${JSON.stringify(state)}

</script>`

)

res.send(html);

});

Conditionally render the components

Earlier, we set the device type in the state on the server-side and pass it to the client-side.

In the client-side, while creating the store, we pass the object we got from the server as the initial state to the configureStore function. This will create the store on the client-side with the same state object from the server.

import { configureStore } from '../server/framework/store';

const initialState = window.__PRELOADED_STATE__ ? window.__PRELOADED_STATE__ : {};

const store = configureStore(initialState);

Now that we have the device type in the store, next step would be to connect to the store from the App shell and use the value to conditionally render the pages/components.

To connect to redux store from a react component, we use the connect method.

Once we get the value from the state as a prop in the component, we can do the conditional rendering of components as mentioned below.

{

this.props.config.deviceType === ‘desktop’ ? <HomePageDesktop/> : <HomePageMobile/>

}

As you can see, the content of the page changes based on the device from which the site was accessed.

Upcoming Work

You might wonder why we have Redux here even though the usage of it has been very minimal in the example.

The above example can be implemented without the help of Redux. We can easily set the device type as a variable in the window object and use it in the components. This method will work fine as long as we are rendering only on the client. The same will be an issue if we are rendering on the server which can be solved by using Redux.

We will get into the implementation of the same in detail in the upcoming articles related to Server Side Rendering, Server Side Data loading. For now, the above method and the architecture help you create Adaptive Web Applications in React.

The codebase of the Adaptive Web Starter Kit is available here.

This article was originally posted here on the Official Blog of Zolostays.