The main goal with this tutorial is learn how to use React to create PWA’s. You will learn to modify index.html and manifest.json files to support PWA. App will load Stock data from Alpha Vantage and loaded data will be shown with Material-UI and Recharts libraries.
Purpose and learning process
In this exercise you will learn the basics of the PWA development with React:
- create a React app with PWA support
- modify index.html
- modify manifest.json
- basic use of chrome development tools with PWA
- fetching stocks value from the web
- use 3th party library to display stocks data in charts
Example screenshots and video
Demo app is a Stock app, which fetches stocks value from the web and display’s data in chart.



Look example video from here: https://youtu.be/z3r9tXPEEq8.
Create a new React project
Create a new React app with create-react-app @ GitHub. You’ll need to have Node 8.16.0 or Node 10.16.0 or later version on your local development machine (but it’s not required on the server).
Use npx, npm or Yarn to create your project.
npx create-react-app stocks-app --template cra-template-pwa
You should see a following information text, if app creating went successfully. Use start
command to start development server and build
command to build your app into static files, which you need to copy to your server and open browser in that location.
Success! Created stocks-app at /Users/pasi/JamkReactProjects/stocks-app
Inside that directory, you can run several commands:
yarn start
Starts the development server.
yarn build
Bundles the app into static files for production.
yarn test
Starts the test runner.
yarn eject
Removes this tool and copies build dependencies, configuration files
and scripts into the app directory. If you do this, you can’t go back!
We suggest that you begin by typing:
cd stocks-app
yarn start
Happy hacking!
Read more about creating React apps: Create React App.
Move web app to PWA
index.js
The production build has all the tools necessary to generate a first-class Progressive Web App, but the offline/cache-first behavior is opt-in only. By default, the build process will generate a service worker file, but it will not be registered, so it will not take control of your production web app.
In order to opt-in to the offline-first behavior, you should look for the following in their src/index.js file and use serviceWorker.register()
to register Service Worker:
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.register();
As the comment states, switching serviceWorker.unregister() to serviceWorker.register() will opt you in to using the service worker and you now have a PWA.
Read more: Making a Progressive Web App.
index.html
Modify public/index.html
to give a correct app name to your PWA. Replace correct <title>
:
<title>Stock App</title>
manifest.json
Modify public/manifest.json
file to have correct name in short_name
and name
attributes:
"short_name": "Stock App",
"name": "Stock App",
You can use a default icons or create a new ones. if you want to replace the icons your app uses, remember place a new icons to public folder.
package.json
If you are planning to publish your app in some sub folder in your web server, then you should open package.json
and add a following homepage
attribute:
{
"name": "stocks-app",
"homepage": "./",
...
Now static files are not looked from the web server’s root. Of course you need to do this, if you are planning to publish your project to some https server and you don’t have access to web root.
Build
You can run your project in local machine using a yarn start
command. This should launch a browser, which opens your app to localhost
.

This is good for local programming and testing, but you need to build a stati files for production and move those files to https
server for testing PWA. Give a command:
yarn build
Publish
Copy your project build
folder contents to web server, which supports HTTPS-connection. Go to your server with a chrome and test how it works from remote server.
Yes – it should work the same way.
Testing PWA
Open development tools and select “Audits”. Leave default options as they are and “Run audits”.

Now your page is tested and you should see there results – more importantly PWA results.

If everything went correctly, you should see that your app is working as a PWA. Scroll results to Progressive Web App and see there detailed results.
Application UI
You can use React UI components to build application UI or use some 3th party libraries – like Material-UI, which will be used in this exercise material.
Example UI with header text and select component:

Install Material-UI to your project:
npm install @material-ui/core --save
Use Select
component to your UI. Learn more here: Select and add following code to your App.js
:
import React, { useState } from 'react';
import './App.css';
// import for Material-UI
import { makeStyles } from '@material-ui/core/styles';
import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@material-ui/core/MenuItem';
import FormControl from '@material-ui/core/FormControl';
import Select from '@material-ui/core/Select';
// styles for Material-UI
const useStyles = makeStyles(theme => ({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}));
function App() {
// material-UI classes
const classes = useStyles();
// hook for stock symbol
const [symbol, setSymbol] = useState('');
// load stocks
const loadStock = (symbol) => {
console.log(symbol);
}
// handle stock symbol change from InputLabel
const handleChange = event => {
setSymbol(event.target.value);
loadStock(event.target.value);
}
return (
<div className="App">
<h1>Stock - Time Series</h1>
<FormControl className={classes.formControl}>
<InputLabel id="demo-simple-select-label">Symbol</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={symbol}
onChange={handleChange}
>
<MenuItem value='AAPL'>Apple</MenuItem>
<MenuItem value='AMZN'>Amazon</MenuItem>
<MenuItem value='NOK'>Nokia</MenuItem>
<MenuItem value='TSLA'>Tesla</MenuItem>
</Select>
</FormControl>
</div>
);
}
export default App;
Test
Test your app. It should now print a selected stock symbol in your browser’s inspector.

Stocks data – Alpha Vantage
You can use any stocks data provider, but now instructions are with Alpha Vantage. Alpha Vantage is composed of a tight-knit community of researchers, engineers, and business professionals. Alpha Vantage Inc. is a leading provider of free APIs for realtime and historical data on stocks, forex (FX), and digital/crypto currencies.
Get a free API key and learn how to get stocks data.
Data url for example with Nokia:
https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=NOK&apikey=YOUR_API_KEY
Returned JSON structure:
{
"Meta Data": {
"1. Information": "Daily Prices (open, high, low, close) and Volumes",
"2. Symbol": "NOK",
"3. Last Refreshed": "2020-02-21",
"4. Output Size": "Compact",
"5. Time Zone": "US/Eastern"
},
"Time Series (Daily)": {
"2020-02-21": {
"1. open": "4.1400",
"2. high": "4.1800",
"3. low": "4.1100",
"4. close": "4.1600",
"5. volume": "21084879"
},
"2020-02-20": {
"1. open": "4.1800",
"2. high": "4.1900",
"3. low": "4.1000",
"4. close": "4.1600",
"5. volume": "25559237"
},
...
You should save a few stock data (AAPL, AMZN, NOK, TSLA) for a testing purposes to your server (for example – student.labranet.jamk.fi). Alpha Vantage is providing a free API service for their global community of users and recommend that you make API requests sparingly (up to 5 API requests per minute and 500 requests per day) to achieve the best server-side performance.
You can use also these:
- https://student.labranet.jamk.fi/~mapas/pwa/stock-app/AAPL.json
- https://student.labranet.jamk.fi/~mapas/pwa/stock-app/AMNZ.json
- https://student.labranet.jamk.fi/~mapas/pwa/stock-app/NOK.json
- https://student.labranet.jamk.fi/~mapas/pwa/stock-app/TSLA.json
Use Axios to load stocks data
Install axios to your project
npm install axios --save
Use axios in loadStock
function to load stock data and display loaded data in browser’s inspector. Use your test data for testing. Remember use real API_KEY
and URL
when you are publishing your app!
const loadStock = (symbol) => {
// use your own server for testing
const URL_PATH = 'https://YOUR_SERVER/PATH/';
const url = URL_PATH + symbol + '.json';
// use in real case
//const API_KEY = 'YOUR_API_KEY';
//const URL_PATH = 'https://www.alphavantage.co/query?function=TIME_SERIES_DAILY';
//const url = URL_PATH + '&symbol='+symbol+'&apikey='+API_KEY;
// use axios to get data
axios.get(url)
.then(res => {
console.log(res.data);
})
}
You will might get CORS policy error. For security reasons, browsers restrict cross-origin HTTP requests initiated from scripts. For example, XMLHttpRequest
and the Fetch API
follows the same-origin policy. This means that a web application using those APIs can only request resources from the same origin the application was loaded from, unless the response from other origins includes the right CORS headers.

Now your application is running in localhost and it can’t load data from your own server by default.
You can run your local browser without web security when developing your app. So you can load data from the different servers.
Start your browsers in macOS:
open -a "Google Chrome" --args --disable-web-security --user-data-dir
Start your browsers in Windows:
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --disable-web-security --user-data-dir="C:/ChromeDevSession"
You should now got loading working and you can continue developing your app in localhost.

Recharts
Install
Use some 3th party charting libraries for React to display time series stocks data. One good one is Recharts. Browser their site and learn how to use it.
Install Recharts to your project:
npm install recharts --save
Understand data
Get familiar with stock data returned by Alpha Vantage. It returns open
, high
, low
and close
price for the stock and the volume
count.
"2020-02-21": {
"1. open": "4.1400",
"2. high": "4.1800",
"3. low": "4.1100",
"4. close": "4.1600",
"5. volume": "21084879"
},
"2020-02-20": {
"1. open": "4.1800",
"2. high": "4.1900",
"3. low": "4.1000",
"4. close": "4.1600",
"5. volume": "25559237"
LineChart
Use LineChart
to show for example open
, close
and high
data.
Import needed classes from recharts
// Rechart
import {
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
ResponsiveContainer
} from 'recharts';
Add a new React Hook to your App.
const [data, setData] = useState([]);
Modify loadStock
function to modify loaded data to work with Rechart. Only 10 first time series data will be used with a following keys: date
, open
, high
, low
, close
and volume
.
const loadStock = (symbol) => {
...
// use axios to get data
axios.get(url)
.then(res => {
// store loaded data to local variable
var data = [];
// take only 10 first
var countMAX = 10;
var count = 0;
// loop keys stored in json like: "2020-02-20", "2020-02-21", etc...
Object.keys(res.data["Time Series (Daily)"]).forEach(function(key) {
// add count and take only countMAX
count++;
if (count < countMAX) {
// push loaded json data to array with keys: data, open, etc...
data.push(
{
'date': key,
'open': res.data["Time Series (Daily)"][key]["1. open"],
'high': res.data["Time Series (Daily)"][key]["2. high"],
'low': res.data["Time Series (Daily)"][key]["3. low"],
'close': res.data["Time Series (Daily)"][key]["4. close"],
'volume': res.data["Time Series (Daily)"][key]["5. volume"],
}
);
}
});
setData(data);
})
}
Render LineChart
below FormControl
in App’s rendering. Use ResponsiveContainer
to get it sized correctly in mobile device’s screen. Learn other elements and attributes from Rechart’s documentation.
<div style={{width: '100%', height: '400px'}}>
<ResponsiveContainer>
<LineChart
data={data}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis domain={['dataMin', 'dataMax']}/>
<Tooltip />
<Legend />
<Line type="monotone" dataKey="open" stroke="#8884d8" activeDot={{ r: 8 }} />
<Line type="monotone" dataKey="close" stroke="#82ca9d" />
<Line type="monotone" dataKey="high" stroke="#ff0000" />
</LineChart>
</ResponsiveContainer>
</div>
Test
Test your app in localhost. It should work now.
Remember
- test with mobile size – use ‘Toggle device tool’
- PWA test

Build and publish
Create a new build and publish your files to server. Remember use https
connection. Test your application in your mobile phone browser (chrome@Android, safari@iOS). It should work now as it described at the start of this tutorial.