Edgio

React

This guide shows you how to serve a React application to Edgio. If you’re using Next.js specifically, we suggest using the Next.js guide.

Example

Here’s an example React app running on Edgio:

Prerequisites

Setup requires:

Install the Edgio CLI

If you have not already done so, install the Edgio CLI.
Bash
1npm i -g @layer0/cli@latest

New project

This guide will use Create React App to generate a project. You can also reference the example app for a complete version of the code.
Bash
1npx create-react-app layer0-cra
2cd layer0-cra
30 init
4# Pick the following options for questions
5# > Add Edgio to the current app
6# Hostname of origin site > layer0-docs-layer0-examples-api-default.layer0-limelight.link
Follow the additional sections below regarding the Create React App setup to finish the project setup.

Existing project

Then, in the root folder of your project, run:
Bash
10 init
This will automatically add all of the required dependencies and files to your project. These include:
  • The @layer0/core package - Allows you to declare routes and deploy your application to Edgio.
  • The @layer0/prefetch package - Allows you to configure a service worker to prefetch and cache pages to improve browsing speed.
  • layer0.config.js - The main configuration file for Edgio.
  • routes.js - A default routes file that sends all requests to React. This file can be updated add caching or proxy URLs to a different origin.

Configure your project

Edgio Router

Using the Router class from @layer0/core, you’ll configure caching for each of your routes, and forward requests to the server module you configured in the previous section using the proxy function.
Note: Change dist to match whatever your configured output file is.

General routes file for React app

JavaScript
1// routes.js
2
3import { Router } from '@layer0/core/router'
4import { BACKENDS } from '@layer0/core'
5
6new Router()
7 .get('/service-worker.js', ({ serviceWorker }) => {
8 serviceWorker('dist/service-worker.js')
9 })
10 .get('/p/:id', ({ cache }) => {
11 // cache product pages at the edge for 1 day
12 cache({
13 edge: {
14 maxAgeSeconds: 60 * 60 * 24, // 1 day
15 },
16 })
17 })
18 .fallback(({ renderWithApp }) => {
19 // send all requests to the server module configured in layer0.config.js
20 renderWithApp()
21 })

Create React App Example

After following the instructions from above, update your routes.js file to match this.
JavaScript
1// routes.js
2
3const { Router } = require('@layer0/core/router')
4
5const ONE_HOUR = 60 * 60
6const ONE_DAY = 24 * ONE_HOUR
7const ONE_YEAR = 365 * ONE_DAY
8
9const edgeOnly = {
10 browser: false,
11 edge: { maxAgeSeconds: ONE_YEAR },
12}
13
14const edgeAndBrowser = {
15 browser: { maxAgeSeconds: ONE_YEAR },
16 edge: { maxAgeSeconds: ONE_YEAR },
17}
18
19export default new Router()
20 .prerender([{ path: '/' }])
21 .match('/api/:path*', ({ cache, proxy }) => {
22 cache(edgeAndBrowser)
23 proxy('origin')
24 })
25 .match('/images/:path*', ({ cache, proxy }) => {
26 cache(edgeAndBrowser)
27 proxy('origin')
28 })
29 .match('/service-worker.js', ({ serviceWorker }) => serviceWorker('build/service-worker.js'))
30 // match routes for js/css resources and serve the static files
31 .match('/static/:path*', ({ serveStatic, cache }) => {
32 cache(edgeAndBrowser)
33 serveStatic('build/static/:path*')
34 })
35 // match client-side routes that aren't a static asset
36 // and serve the app shell. client-side router will
37 // handle the route once it is rendered
38 .match('/:path*/:file([^\\.]+|)', ({ appShell, cache }) => {
39 cache(edgeOnly)
40 appShell('build/index.html')
41 })
42 // match other assets such as favicon, manifest.json, etc
43 .match('/:path*', ({ serveStatic, cache }) => {
44 cache(edgeOnly)
45 serveStatic('build/:path*')
46 })
47 // send any unmatched request to origin
48 .fallback(({ serveStatic }) => serveStatic('build/index.html'))

Prefetching

Install the @layer0/react to enable this feature.
Bash
1npm i -D @layer0/react
Add the Prefetch component from @layer0/react to your links to cache pages before the user clicks on them. Here’s an example:
JavaScript
1import { Link } from 'react-router'
2import { Prefetch } from '@layer0/react'
3
4export default function ProductListing() {
5 return (
6 <div>
7 {/* ... */}
8 {/* The URL you need to prefetch is the API call that the page component will make when it mounts. It will vary based on how you've implemented your site. */}
9 <Prefetch url="/api/products/1.json">
10 <Link to="/p/1">Product 1</Link>
11 </Prefetch>
12 {/* ... */}
13 </div>
14 )
15}
By default, Prefetch waits until the link appears in the viewport before prefetching. You can prefetch immediately by setting the immediately prop:
JavaScript
1<Prefetch url="/api/products/1.json" immediately>
2 <Link to="/p/1">Product 1</Link>
3</Prefetch>

Service Worker

In order for prefetching to work, you need to configure a service worker that uses the Prefetcher class from @layer0/prefetch.
Following the Create React App example from above? Make sure to create a file in src/service-worker.js. Paste the code example below into that file.
Here is an example service worker:
JavaScript
1import { skipWaiting, clientsClaim } from 'workbox-core'
2import { precacheAndRoute } from 'workbox-precaching'
3import DeepFetchPlugin from '@layer0/prefetch/sw/DeepFetchPlugin'
4import { Prefetcher } from '@layer0/prefetch/sw'
5
6skipWaiting()
7clientsClaim()
8precacheAndRoute(self.__WB_MANIFEST || [])
9
10new Prefetcher({
11 plugins: [
12 // Enable this as part of the example in this guide
13 // new DeepFetchPlugin([
14 // {
15 // jsonQuery: 'picture',
16 // as: 'image',
17 // },
18 // ]),
19 ],
20}).route()
In order to install the service worker in the browser when your site loads, call the install function from @layer0/prefetch.
JavaScript
1import { install } from '@layer0/prefetch/window'
2
3install()
If following the Create React App example, this can be done in the same location as App initialization in index.js after the ReactDOM.render call.

Server Side Rendering

React offers a great amount of flexibility in how you set up server side rendering. Frameworks like Next.js offer a standardized, built-in way of implementing SSR. If you’re using Next.js specifically, we suggest using the Next.js guide. We’ll assume at this point that you’re not using Next.js, but have an existing Node app that is doing server-side rendering.
In order to render on Edgio, you need to provide a function that takes a Node Request and Response and sends the HTML that results from the renderToString() method from react-dom/server. Configure that function using the server property of layer0.config.js. Here’s an example:
JavaScript
1// layer0.config.js
2
3module.exports = {
4 server: {
5 path: '0/server.js',
6 },
7}
JavaScript
1// server.js - basic node example
2
3const ReactDOMServer = require('react-dom/server')
4const App = require('./app')
5
6module.exports = function server(request, response) {
7 const html = ReactDOMServer.renderToString(React.createElement(App, { url: request.url }))
8 response.set('Content-Type', 'text/html')
9 response.send(html)
10}

Express Example

If you already have an express app set up to do server side rendering, the server module can also export that instead:
JavaScript
1// server.js - express example
2
3const express = require('express')
4const app = express()
5const ReactDOMServer = require('react-dom/server')
6const App = require('./app')
7
8app.use((request, response, next) => {
9 const html = ReactDOMServer.renderToString(React.createElement(App, { url: request.url }))
10 response.set('Content-Type', 'text/html')
11 response.send(html)
12})
13
14module.exports = app

Bundling your server with Webpack

We recommend bundling your server with Webpack. Your webpack config should use the following settings:
JavaScript
1module.exports = {
2 target: 'node',
3 mode: 'production',
4 output: {
5 filename: '[name].js',
6 path: path.resolve(__dirname, '..', 'dist'), // should match server.path in layer0.config.js
7 libraryTarget: 'umd',
8 libraryExport: 'default',
9 },
10 entry: {
11 server: './layer0/server.js', // this should point to your server entry point, which should export a function of type (request: Request, response: Response) => void or an express app as the default export.
12 },
13}

Deploying

Deploy your app to the Sites by running the following command in your project’s root directory:
Bash
10 deploy
If you have a static app or are following the above example then you need to build the app first
Bash
1npm run build
20 deploy
For more on deploying, see Deploying.