Chapter 1. Creating Applications
React is a surprisingly adaptable development framework. Developers use it to create large JavaScript-heavy Single-Page Applications (SPAs) or to build surprisingly small plug-ins. You can use it to embed code inside a Rails application or generate a content-rich website.
In this chapter, we look at the various ways of creating a React application. We also look at some of the more valuable tools you might want to add to your development cycle. Few people now create their JavaScript projects from scratch. Doing so is a tedious process, involving an uncomfortable amount of tinkering and configuration. The good news is that you can use a tool to generate the code you need in almost every case.
Let’s take a whistle-stop tour of the many ways of starting your React journey, beginning with the one most frequently used:
create-react-app
.
1.1 Generate a Simple Application
Problem
React projects are challenging to create and configure from scratch. Not only are there numerous design choices to make—which libraries to include, which tools to use, which language features to enable—but manually created applications will, by their nature, differ from one another. Project idiosyncrasies increase the time it takes a new developer to become productive.
Solution
create-react-app
is a tool for building SPAs with a standard structure and a good set of default options. Generated projects use
the React Scripts library to build, test, and run the code. Projects have a standard Webpack configuration and a standard set of
language features enabled.
Any developer who has worked on one create-react-app
application instantly feels at home with any other. They understand the
project structure and know which language features they can use. It is simple to use and contains all the features that a typical
application requires: from Babel configuration and file loaders to testing libraries and a development server.
If you’re new to React, or need to create a generic SPA with the minimum of fuss, then you should consider creating your app with
create-react-app
.
You can choose to install the create-react-app
command globally on your machine, but this is now discouraged. Instead, you should
create a new project by calling create-react-app
via npx
. Using npx
ensures you’re building your application with the latest
version of create-react-app
:
$
npxcreate-react-app
my-app
This command creates a new project directory called my-app. By default, the application uses JavaScript. If you want to use
TypeScript as your development language, create-react-app
provides that as an option:
$
npxcreate-react-app
--template
typescript
my-app
Facebook developed create-react-app
, so it should come as no surprise that if you have the yarn
package manager installed, then
your new project will use yarn
by default. To use npm
, you can either specify the --use-npm
flag or change into the directory
and remove the yarn.lock file and then rerun the install with npm
:
$
cd
my-app
$
rmyarn.lock
$
npminstall
To start your application, run the start
script:
$
npmstart
# or yarn start
This command launches a server on port 3000 and opens a browser at the home page, as shown in Figure 1-1.
The server delivers your application as a single, large bundle of JavaScript. The code mounts all of its components inside this
<div/>
in public/index.html:
<
div
id
=
"root"
></
div
>
The code to generate the components begins in the src/index.js file (src/index.tsx if you’re using TypeScript):
import
React
from
'react'
import
ReactDOM
from
'react-dom'
import
'./index.css'
import
App
from
'./App'
import
reportWebVitals
from
'./reportWebVitals'
ReactDOM
.
render
(
<
React
.
StrictMode
>
<
App
/>
</
React
.
StrictMode
>,
document
.
getElementById
(
'root'
)
)
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals
()
This file does little more than render a single component called <App/>
, which it imports from App.js (or App.tsx) in the same
directory:
import
logo
from
'./logo.svg'
import
'./App.css'
function
App
()
{
return
(
<
div
className
=
"App"
>
<
header
className
=
"App-header"
>
<
img
src
=
{
logo
}
className
=
"App-logo"
alt
=
"logo"
/>
<
p
>
Edit
<
code
>
src
/
App
.
js
</
code
>
and
save
to
reload
.
</
p
>
<
a
className
=
"App-link"
href
=
"https://reactjs.org"
target
=
"_blank"
rel
=
"noopener noreferrer"
>
Learn
React
</
a
>
</
header
>
</
div
>
)
}
export
default
App
If you edit this file while the application is start
-ed, the page in the browser automatically updates.
When you’re ready to ship the code to production, you need to generate a set of static files that you can deploy on a standard web
server. To do this, run the build
script:
$
npmrun
build
The build
script creates a build directory and then publishes a set of static files (see Figure 1-2).
The build copies many of these files from the public/ directory. The code for the app is transpiled into browser-compatible JavaScript and stored in one or more files in the static/js directory. Stylesheets used by the application are stitched together and stored in static/css. Several of the files have hashed IDs added to them so that when you deploy your application, browsers download the latest code rather than some old cached version.
Discussion
create-react-app
is not just a tool for generating a new application but also a platform to keep your React application up-to-date
with the latest tools and libraries. You can upgrade the react-scripts
library as you would any other: by changing the version
number and rerunning npm install
. You don’t need to manage a list of Babel plugins or postcss libraries, or maintain a complex
webpack.config.js file. The react-scripts
library manages them all for you.
The configuration is all still there, of course, but buried deep within the react-scripts directory. In there, you will find the webpack.config.js file, containing all the Babel configuration and file loaders that your application will use. Because it’s a library, you can update React Scripts just as you would any other dependency.
If, however, you later decide to manage all of this yourself, you’re free to do so. If you eject the application, then everything comes back under your control:
$
npmrun
eject
However, this is a one-time-only change. Once you have ejected your application, there is no going back. You should think carefully
before ever ejecting an application. You may find that the configuration you need is already available. For example, developers
would often eject an application to switch to using TypeScript. The --template typescript
option now removes the need for that.
Another common reason for ejecting was to proxy web services. React apps often need to connect to some separate API backend. Developers used to do this by configuring Webpack to proxy a remote server through the local development server. You can now avoid doing this by setting a proxy in the package.json file:
"proxy"
:
"http://myapiserver"
,
If your code now contacts a URL that the server cannot find locally (/api/thing), the react-scripts
automatically proxies
these requests to http://myapiserver/api/thing.
Tip
If you can, avoid ejecting your application. Look through the create-react-app
documentation to see if you can make the change some other way.
You can download the source for this recipe in JavaScript or TypeScript from the GitHub site.
1.2 Build Content-Rich Apps with Gatsby
Problem
Content-rich sites like blogs and online stores need to serve large amounts of complex content efficiently. A tool like
create-react-app
is not suitable for this kind of website because it delivers everything as a single large bundle of JavaScript
that a browser must download before anything displays.
Solution
If you are building a content-rich site, consider using Gatsby.
Gatsby focuses on loading, transforming, and delivering content in the most efficient way possible. It can generate static versions
of web pages, which means that the response times of Gatsby sites are often significantly slower than, say, those built with
create-react-app
.
Gatsby has many plugins that can load and transform data efficiently from static local data, GraphQL sources, and third-party content management systems such as WordPress.
You can install gatsby
globally, but you can also run it via the npx
command:
$
npxgatsby
new
my-app
The gatsby new
command creates a new project in a subdirectory called my-app. The first time you run this command, it asks which
package manager to use: either yarn
or npm
.
To start your application, change into the new directory and run it in development mode:
$
cd
my-app
$
npmrun
develop
You can then open your application at http://localhost:8000, as shown in Figure 1-3.
Gatsby projects have a straightforward structure, as shown in Figure 1-4.
The core of the application lives under the src directory. Each page within a Gatsby app has its own React component. This is the front page of the default application in index.js:
import
*
as
React
from
"react"
import
{
Link
}
from
"gatsby"
import
{
StaticImage
}
from
"gatsby-plugin-image"
import
Layout
from
"../components/layout"
import
Seo
from
"../components/seo"
const
IndexPage
=
()
=>
(
<
Layout
>
<
Seo
title
=
"Home"
/>
<
h1
>
Hi
people
</
h1
>
<
p
>
Welcome
to
your
new
Gatsby
site
.</
p
>
<
p
>
Now
go
build
something
great
.</
p
>
<
StaticImage
src
=
"../images/gatsby-astronaut.png"
width
=
{
300
}
quality
=
{
95
}
formats
=
{[
"AUTO"
,
"WEBP"
,
"AVIF"
]}
alt
=
"A Gatsby astronaut"
style
=
{{
marginBottom
:
`1.45rem`
}}
/>
<
p
>
<
Link
to
=
"/page-2/"
>
Go
to
page
2
</
Link
>
<
br
/>
<
Link
to
=
"/using-typescript/"
>
Go
to
"Using TypeScript"
</
Link
>
</
p
>
</
Layout
>
)
export
default
IndexPage
There is no need to create a route for the page. Each page component is automatically assigned a route. For example, the page at src/pages/using-typescript.tsx is automatically available at using-typescript.1 This approach has multiple advantages. First, if you have many pages, you don’t need to manage the routes for them manually. Second, it means that Gatsby can deliver much more rapidly. To see why, let’s look at how to generate a production build for a Gatsby application.
If you stop the Gatsby development server,2 you can generate a production build with the following:
$
npmrun
build
This command runs a gatsby build
command, which creates a public directory. And it is the public directory that contains the
real magic of Gatsby. For each page, you find two files. First, a generated JavaScript file:
1389 06:48 component---src-pages-using-typescript-tsx-93b78cfadc08d7d203c6.js
Here you can see that the code for using-typescript.tsx is just 1,389 bytes long, which, with the core framework, is just enough
JavaScript to build the page. It is not the kind of include-everything script that you find in a create-react-app
project.
Second, there is a subdirectory for each page containing a generated HTML file. For using-typescript.tsx, the file is called public/using-typescript/index.html, containing a statically generated version of the web page. It contains the HTML that the using-typescript.tsx component would otherwise render dynamically. At the end of the web page, it loads the JavaScript version of the page to generate any dynamic content.
This file structure means that Gatsby pages load as quickly as static pages. Using the bundled react-helmet
library, you can also
generate <meta/>
header tags with additional features about your site. Both features are great for search engine optimization (SEO).
Discussion
How will the content get into your Gatsby application? You might use a headless content management system, a GraphQL service, a static data source, or something else. Fortunately, Gatsby has many plugins that allow you to connect data sources to your application and then transform the content from other formats, such as Markdown, into HTML.
You can find a complete set of plugins on the Gatsby website.
Most of the time, you choose the plugins you need when you first create the project. To give you a head start, Gatsby also supports start templates. The template provides the initial application structure and configuration. The app we built earlier uses the default starter template, which is quite simple. The gatsby-config.js file in the root of the application configures which plugins your application uses.
But there are masses of Gatsby starters available, preconfigured to build applications that connect to various data sources, with preconfigured options for SEO, styling, offline caching, progressive web applications (PWAs), and more. Whatever kind of content-rich application you are building, there is a starter close to what you need.
There is more information on the Gatsby website about Gatsby starters, as well as a cheat sheet for the most useful tools and commands.
You can download the source for this recipe from the GitHub site.
1.3 Build Universal Apps with Razzle
Problem
Sometimes when you start to build an application, it is not always clear what the significant architectural decisions will be. Should you create an SPA? If performance is critical, should you use server side r? You will need to decide what your deployment platform will be and whether you will write your code in JavaScript or TypeScript.
Many tools require that you answer these questions early on. If you later change your mind, modifying how you build and deploy your application can be complicated.
Solution
If you want to defer decisions about how you build and deploy your application, you should consider using Razzle.
Razzle is a tool for building Universal applications: applications that can execute their JavaScript on the server. Or the client. Or both.
Razzle uses a plugin architecture that allows you to change your mind about how you build your application. It will even let you change your mind about building your code in React, Preact, or some other framework entirely, like Elm or Vue.
You can create a Razzle application with the create-razzle-app
command:3
$
npxcreate-razzle-app
my-app
This command creates a new Razzle project in the my-app subdirectory. You can start the development server with the start
script:
$
cd
my-app
$
npmrun
start
The start
script will dynamically build both client code and server code and then run the server on port 3000, as shown in Figure
1-5.
When you want to deploy a production version of your application, you can then run the build
script:
$
npmrun
build
Unlike create-react-app
, this will build not just the client code but also a Node server. Razzle generates the code in the
build subdirectory. The server code will continue to generate static code for your client at runtime. You can start a production
server by running the build/server.js file using the start:prod
script:
$
npmrun
start:prod
You can deploy the production server anywhere that Node is available.
The server and the client can both run the same code, which makes it Universal. But how does it do this?
The client and the server have different entry points. The server runs the code in src/server.js; the browser runs the code in src/client.js. Both server.js and client.js then render the same app using src/App.js.
If you want to run your app as an SPA, remove the src/index.js and src/server.js files. Then create an index.html file in the
public folder containing a <div/>
with an ID of root
, and rebuild the application with this:
$
node_modules/.bin/razzlebuild
--type
=
spa
Tip
To build your application as an SPA every time, add --type=spa
to the start
and build
scripts in package.json.
You will generate a full SPA in build/public/ that you can deploy on any web server.
Discussion
Razzle is so adaptable because it is built from a set of highly configurable plugins. Each plugin is a higher-order function that receives a Webpack configuration and returns a modified version. One plugin might transpile TypeScript code; another might bundle the React libraries.
If you want to switch your application to Vue, you only need to replace the plugins you use.
You can find a list of available plugins on the Razzle website.
You can download the source for this recipe from the GitHub site.
1.4 Manage Server and Client Code with Next.js
Solution
Next.js is a tool for generating React applications and server code. The API end-points and the client pages use default routing conventions, making them simpler to build and deploy than they would be if you manage them yourself. You can find full details about Next.js on the website.
You can create a Next.js application with this command:
$
npxcreate-next-app
my-app
This will use yarn
as the package manager if you have it installed. You can force it to use the npm
package manager with the
--user-npm
flag:
$
npxcreate-next-app
--use-npm
my-app
This will create a Next.js application in the my-app subdirectory. To start the app, run the dev
script (see Figure 1-6):
$
cd
my-app
$
npmrun
dev
Next.js allows you to create pages without the need to manage any routing configuration. If you add a component script to the pages folder, it will instantly become available through the server. For example, the pages/index.js component generates the home page of the default application.
This approach is similar to the one taken by Gatsby,4 but is taken further in Next.js to include server-side code.
Next.js applications usually include some API server code, which is unusual for React applications, which are often built separately from server code. But if you look inside pages/api, you will find an example server endpoint called hello.js:
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
export
default
(
req
,
res
)
=>
{
res
.
status
(
200
).
json
({
name
:
'John Doe'
})
}
The routing that mounts this to the endpoint api/hello happens automatically.
Next.js transpiles your code into a hidden directory called .next, which it can then deploy to a service such as Next.js’s own Vercel platform.
If you want, you generate a static build of your application with:
$
node_modules/.bin/next
export
The export
command will build your client code in a directory called out. The command will convert each page into a statically
rendered HTML file, which will load quickly in the browser. At the end of the page, it will load the JavaScript version to
generate any dynamic content.
Warning
If you create an exported version of a Next.js application, it won’t include any server-side APIs.
Next.js comes with a bunch of data-fetching options, which allow you to get data from static content, or via headless content management system (CMS) sources.
Discussion
Next.js is in many ways similar to Gatsby. Its focus is on the speed of delivery, with a small amount of configuration. It’s probably most beneficial for teams that will have very little server code.
You can download the source for this recipe from the GitHub site.
1.5 Create a Tiny App with Preact
Solution
If you want React features but don’t want to pay the price of a React-size JavaScript bundle, consider using Preact.
Preact is not React. It is a separate library, designed to be as close to React as possible but much smaller.
The reason that the React framework is so big is because of the way it works. React components don’t generate elements in the Document Object Model (DOM) of the browser directly. Instead, they build elements within a virtual DOM and then update the actual DOM at frequent intervals. Doing so allows basic DOM rendering to be fast because the actual DOM needs to be updated only when there are actual changes. However, it does have a downside. React’s virtual DOM requires a lot of code to keep it up-to-date. It needs to manage an entire synthetic event model, which parallels the one in the browser. For this reason, the React framework is large and can take some time to download.
One way around this is to use techniques such as SSR, but SSR can be complex to configure.5 Sometimes, you want to download a small amount of code. And that’s why Preact exists.
The Preact library, although similar to React, is tiny. At the time of writing, the main Preact library is around 4KB, which is small enough that it’s possible to add React-like features to web pages in barely more code than is required to write native JavaScript.
Preact lets you choose how to use it: as a small JavaScript library included in a web page (the no-tools approach) or as a full-blown JavaScript application.
The no-tools approach is basic. The core Preact library does not support JSX, and you will have no Babel support, so you will not be able to use modern JavaScript. Here is an example web page using the raw Preact library:
<
html
>
<
head
>
<
title
>
No Tools!</
title
>
<
script
src
=
"https://unpkg.com/preact?umd"
></
script
>
</
head
>
<
body
>
<
h1
>
No Tools Preact App!</
h1
>
<
div
id
=
"root"
></
div
>
<
script
>
var
h
=
window
.
preact
.
h
;
var
render
=
window
.
preact
.
render
;
var
mount
=
document
.
getElementById
(
'root'
);
render
(
h
(
'button'
,
{
onClick
:
function
()
{
render
(
h
(
'div'
,
null
,
'Hello'
),
mount
);
}
},
'Click!'
),
mount
);
</
script
>
</
body
>
</
html
>
This application will mount itself at the <div/>
with an ID of root
, where it will display a button. When you click the button, it
will replace the contents of the root div
with the string "Hello"
, which is about as basic as a Preact app can be.
You would rarely write an application in this way. In reality, you would create a simple build-chain that would, at the least, support modern JavaScript.
Preact supports the entire spectrum of JavaScript applications. At the other extreme, you can create a complete Preact application
with preact-cli
.
preact-cli
is a tool for creating Preact projects and is analogous to tools like create-react-app
. You can create a Preact
application with:
$
npxpreact-cli
create
default
my-app
Tip
This command uses the default template. Other templates are available for creating projects using, for example, Material components or TypeScript. See the Preact GitHub page for more information.
This command will create your new Preact application in the my-app subdirectory. To start it, run the dev
script:
$
cd
my-app
$
npmrun
dev
The server will run on port 8080, as shown in Figure 1-7.
The server generates a web page, which calls back for a JavaScript bundle made from the code in src/index.js.
You now have a full-scale React-like application. The code inside the Home
component (src/routes/home/index.js), for example,
looks very React-like, with full JSX support:
import
{
h
}
from
'preact'
;
import
style
from
'./style.css'
;
const
Home
=
()
=>
(
<
div
class
=
{
style
.
home
}>
<
h1
>
Home
</
h1
>
<
p
>
This
is
the
Home
component
.</
p
>
</
div
>
);
export
default
Home
;
The only significant difference from a standard React component is that a function called h
is imported from the preact
library, instead of importing React
from the react
library.
Note
The JSX within the Preact code will be converted into a series of calls to the h
function, which is why it needs to be
imported. For the same reason, applications created with create-react-app
prior to version 17 also required the import of the
react
object. From version 17 create-react-app
switched to use the
JSX transform, doing away for the need to import react
every time. It’s always possible that future versions of Preact will make a similar change.
However, the size of the application has increased: it is now a little over 300KB. That’s pretty large, but we are still in dev
mode. To see the real power of Preact, stop the dev server by pressing Ctrl-C, and then run the build
script:
$
npmrun
build
This command will generate a static version of the application in the build directory. First, this will have the advantage of creating a static copy of the front page, which will render quickly. Second, it will remove all unused code from the application and shrink everything down. If you serve this built version of the app on a standard web server, the browser will transfer only about 50–60KB when it’s opened.
Discussion
Preact is a remarkable project. Despite working in a very different way from React, it provides virtually the same power at a fraction of the size. And the fact that you can use it for anything from the lowliest inline code to a full-blown SPA means it is well worth considering if code size is critical to your project.
You can find out more about Preact on the Preact website.
You can download the source for the no-tools example and the larger Preact example from the GitHub site.
If you would like to make Preact look even more like React, see the preact-compat library.
Finally, for a project that takes a similar approach to Preact, look at InfernoJS.
1.6 Build Libraries with nwb
Problem
Large organizations often develop several React applications at the same time. If you’re a consultancy, you might create applications for multiple organizations. If you’re a software house, you might create various applications that require the same look and feel, so you will probably want to build shared components to use across several applications.
When you create a component project, you need to create a directory structure, select a set of tools, choose a set of language features, and create a build chain that can bundle your component in a deployable format. This process can be just as tedious as manually creating a project for an entire React application.
Solution
You can use the nwb
toolkit to create complete React applications or single React components. It can also create components for use
within Preact and InfernoJS projects, but we concentrate on React components here.
To create a new React component project, you will first need to install the nwb
tool globally:
$
npminstall
-g
nwb
You can then create a new project with the nwb
command:
$
nwbnew
react-component
my-component
Note
If instead of creating a single component, you want to create an entire nwb
application, you can replace react-component
in
this command with react-app
, preact-app
, or inferno-app
to create an application in the given framework. You can also use
vanilla-app
if you want to create a basic JavaScript project without a
framework.
When you run this command, it will ask you several questions about the type of library you want to build. For example, it will ask you if you’re going to build ECMAScript modules:
Creating a react-component project...
? Do you want to create an ES modules build? (Y/n)
This option allows you to build a version including an export
statement, which Webpack can use to decide if it needs to include
the component in a client application. You will also be asked if you want to create a Universal Module Definition (UMD):
? Do you want to create a UMD build? (y/N)
That’s useful if you want to include your component in a <script/>
within a web page. For our example, we won’t create a UMD
build.
After the questions, the tool will create an nwb
component project inside the my-component subdirectory. The project comes with a
simple wrapper application that you can start with the start
script:
$
cd
my-component
$
npmrun
start
The demo application runs on port 3000, as shown in Figure 1-8.
The application will contain a single component defined in src/index.js:
import
React
,
{
Component
}
from
'react'
export
default
class
extends
Component
{
render
()
{
return
(
<
div
>
<
h2
>
Welcome
to
React
components
</
h2
>
</
div
>
)
}
}
You can now build the component as you would any React project. When you are ready to create a publishable version, type:
$
npmrun
build
The built component will be in lib/index.js, which you can deploy to a repository for use within other projects.
Discussion
For further details on creating nwb
components, see the nwb
guide to developing components and libraries.
You can download the source for this recipe from the GitHub site.
1.7 Add React to Rails with Webpacker
Problem
The Rails framework was created before interactive JavaScript applications became popular. Rails applications follow a more traditional model for web application development, in which it generates HTML pages on the server in response to browser requests. But sometimes, you may want to include more interactive elements inside a Rails application.
Solution
You can use the Webpacker library to insert React applications into Rails-generated web pages. To see how it works, let’s first generate a Rails application that includes Webpacker:
$
railsnew
my-app
--webpack
=
react
This command will create a Rails application in a directory called my-app that is preconfigured to run a Webpacker server. Before we start the application, let’s go into it and generate an example page/controller:
$
cd
my-app
$
railsgenerate
controller
Example
index
That code will generate this template page at app/views/example/index.html.erb:
<
h1
>
Example#index</
h1
>
<
p
>
Find me in app/views/example/index.html.erb</
p
>
Next, we need to create a small React application that we can insert into this page. Rails inserts Webpacker applications as packs: small JavaScript bundles within Rails. We’ll create a new pack in app/javascript/packs/counter.js containing a simple counter component:
import
React
,
{
useState
}
from
'react'
import
ReactDOM
from
'react-dom'
const
Counter
=
(
props
)
=>
{
const
[
count
,
setCount
]
=
useState
(
0
)
return
(
<
div
className
=
"Counter"
>
You
have
clicked
the
button
{
count
}
times
.
<
button
onClick
=
{()
=>
setCount
((
c
)
=>
c
+
1
)}>
Click
!
</
button
>
</
div
>
)
}
document
.
addEventListener
(
'DOMContentLoaded'
,
()
=>
{
ReactDOM
.
render
(
<
Counter
/>,
document
.
body
.
appendChild
(
document
.
createElement
(
'div'
))
)
})
This application updates a counter every time a user clicks the button.
We can now insert the pack into the web page by adding a single line of code to the template page:
<
h1
>
Example#index</
h1
>
<
p
>
Find me in app/views/example/index.html.erb</
p
>
<
%= javascript_pack_tag 'counter' %>
Finally, we can run the Rails server on port 3000:
$
railsserver
Warning
At the time of writing, you will need the yarn
package manager installed when starting the server. You can install yarn
globally with npm install -g yarn
.
You will see the http://localhost:3000/example/index.html page in Figure 1-9.
Discussion
Behind the scenes, as you have probably guessed, Webpacker transforms the application using a copy of Webpack, which you can configure with the app/config/webpacker.yml config file.
Webpacker is used alongside Rails code rather than as a replacement for it. You should consider using it if your Rails application requires a small amount of additional interactivity.
You can find out more about Webpacker on the Webpacker GitHub site.
You can download the source for this recipe from the GitHub site.
1.8 Create Custom Elements with Preact
Problem
There are sometimes circumstances where it is challenging to add React code into existing content. For example, in some CMS configurations, users are not allowed to insert additional JavaScript into the body of a page. In these cases, it would be helpful to have some standardized way to insert JavaScript applications safely into a page.
Solution
Custom elements are a standard way of creating new HTML elements you can use on a web page. In effect, they extend the HTML language by making more tags available to a user.
This recipe looks at how we can use a lightweight framework like Preact to create custom elements, which we can publish on a third-party server.
Let’s begin by creating a new Preact application. This application will serve the custom element that we will be able to use elsewhere:6
$
preactcreate
default
my-element
Now we will change into the app’s directory and add the preact-custom-element
library to the project:
$
cd
my-element
$
npminstall
preact-custom-element
The preact-custom-element
library will allow us to register a new custom HTML element in a browser.
Next, we need to modify the src/index.js file of the Preact project so that it registers a new custom element, which we will call components/Converter/index.js:
import
register
from
'preact-custom-element'
import
Converter
from
'./components/Converter'
register
(
Converter
,
'x-converter'
,
[
'currency'
])
The register
method tells the browser that we want to create a new custom HTML element called <x-converter/>
that has a single
property called currency
, which we will define in src/components/Converter/index.js:
import
{
h
}
from
'preact'
import
{
useEffect
,
useState
}
from
'preact/hooks'
import
'style/index.css'
const
rates
=
{
gbp
:
0.81
,
eur
:
0.92
,
jpy
:
106.64
}
export
default
({
currency
=
'gbp'
})
=>
{
const
[
curr
,
setCurr
]
=
useState
(
currency
)
const
[
amount
,
setAmount
]
=
useState
(
0
)
useEffect
(()
=>
{
setCurr
(
currency
)
},
[
currency
])
return
(
<
div
className
=
"Converter"
>
<
p
>
<
label
htmlFor
=
"currency"
>
Currency
:
</
label
>
<
select
name
=
"currency"
value
=
{
curr
}
onChange
=
{(
evt
)
=>
setCurr
(
evt
.
target
.
value
)}
>
{
Object
.
keys
(
rates
).
map
((
r
)
=>
(
<
option
value
=
{
r
}>{
r
}</
option
>
))}
</
select
>
</
p
>
<
p
className
=
"Converter-amount"
>
<
label
htmlFor
=
"amount"
>
Amount
:
</
label
>
<
input
name
=
"amount"
size
=
{
8
}
type
=
"number"
value
=
{
amount
}
onInput
=
{(
evt
)
=>
setAmount
(
parseFloat
(
evt
.
target
.
value
))}
/>
</
p
>
<
p
>
Cost
:
{((
amount
||
0
)
/
rates
[
curr
]).
toLocaleString
(
'en-US'
,
{
style
:
'currency'
,
currency
:
'USD'
,
})}
</
p
>
</
div
>
)
}
Note
To be compliant with the custom elements specification, we must choose a name for our element that begins with a lowercase letter, does not include any uppercase letters, and contains a hyphen.7 This convention ensures the name does not clash with any standard element name.
Our Converter
component is a currency converter, which in our example uses a fixed set of exchange rates. If we now start our
Preact server:
$
npmrun
dev
the JavaScript for the custom element will be available at http://localhost:8080/bundle.js.
To use this new custom element, let’s create a static web page somewhere with this HTML:
<
html
>
<
head
>
<
script
src
=
"https://unpkg.com/babel-polyfill/dist/polyfill.min.js"
>
</
script
>
<
script
src
=
"https://unpkg.com/@webcomponents/webcomponentsjs"
>
</
script
>
<!--
Replace
this
with
the
address
of
your
custom
element
-->
<
script
type
=
"text/javascript"
src
=
"http://localhost:8080/bundle.js"
>
</
script
>
</
head
>
<
body
>
<
h1
>
Custom
Web
Element
</
h1
>
<
div
style
=
"float: right; clear: both"
>
<!--
This
tag
will
insert
the
Preact
app
-->
<
x
-
converter
currency
=
"jpy"
/>
</
div
>
<
p
>
This
page
contains
an
example
custom
element
called
<
code
>
&
lt
;
x
-
converter
/&
gt
;</
code
>,
which
is
being
served
from
a
different
location
</
p
>
</
body
>
</
html
>
This web page includes the definition of the custom element in the final <script/>
of the <head/>
element. To ensure that
the custom element is available across both new and old browsers, we also include a couple of shims from unpkg.com.
Now that we’ve included the custom element code in the web page, we can insert
<x-converter/>
tags into the code, as if they are
part of standard HTML. In our example, we are also passing a currency
property to the underlying Preact
component.
Warning
Custom element properties are passed to the underlying component with lowercase names, regardless of how we define them in the HTML.
We can run this page through a web server, separate from the Preact server. Figure 1-10 shows the new custom element.
Discussion
The custom element does not need to be on the same server as the web page that uses it, which means that we can use custom elements
to publish widgets for any web page. Because of this, you might want to check the Referer
header on any incoming request to the
component to prevent any unauthorized usage.
Our example is serving the custom element from Preact’s development server. For a production release, you would probably want to create a static build of the component, which will likely be significantly smaller.8
You can download the source for this recipe from the GitHub site.
1.9 Use Storybook for Component Development
Problem
React components are the stable building material of React applications. If we write them carefully, we can reuse the components in other React applications. But when you build a component, it takes work to check how it works in all circumstances. For example, in an asynchronous application, React might render the component with undefined properties. Will the component still render correctly? Will it show errors?
But if you are building components as part of a complex application, it can be tough to create all of the situations with which your component will need to cope.
Also, if you have specialized user experience (UX) developers working on your team, it can waste a lot of time if they have to navigate through an application to view the single component they have in development.
It would be helpful if there were some way of displaying a component in isolation and passing it example sets of properties.
Solution
Storybook is a tool for displaying libraries of components in various states. You could describe it as a gallery for components, but that’s probably selling it short. In reality, Storybook is a tool for component development.
How do we add Storybook to a project? Let’s begin by creating a React application with create-react-app
:
$
npxcreate-react-app
my-app
$
cd
my-app
Now we can add Storybook to the project:
$
npxsb
init
We then start the Storybook server with yarn
or npm
:
$
npmrun
storybook
Storybook runs a separate server on port 9000, as you can see in Figure 1-11. When you use Storybook, there is no need to run the actual React application.
Storybook calls a single component rendered with example properties a story. The default installation of Storybook generates sample stories in the src/stories directory of the application. For example, this is src/stories/Button.stories.js:
import
React
from
'react'
;
import
{
Button
}
from
'./Button'
;
export
default
{
title
:
'Example/Button'
,
component
:
Button
,
argTypes
:
{
backgroundColor
:
{
control
:
'color'
},
},
};
const
Template
=
(
args
)
=>
<
Button
{
...args
}
/>;
export
const
Primary
=
Template
.
bind
({});
Primary
.
args
=
{
primary
:
true
,
label
:
'Button'
,
};
export
const
Secondary
=
Template
.
bind
({});
Secondary
.
args
=
{
label
:
'Button'
,
};
export
const
Large
=
Template
.
bind
({});
Large
.
args
=
{
size
:
'large'
,
label
:
'Button'
,
};
export
const
Small
=
Template
.
bind
({});
Small
.
args
=
{
size
:
'small'
,
label
:
'Button'
,
};
Storybook watches for files named *.stories.js in your source folder, and it doesn’t care where they are, so you are free to create them where you like. One typical pattern places the stories in a folder alongside the component they are showcasing. So if you copy the folder to a different application, you can include stories as living documentation.
Figure 1-12 shows what Button.stories.js looks like in Storybook.
Discussion
Despite its simple appearance, Storybook is a productive development tool. It allows you to focus on one component at a time. Like a kind of visual unit test, it enables you to try a component in a series of possible scenarios to check that it behaves appropriately.
Storybook also has a large selection of additional add-ons.
The add-ons allow you to:
-
Check for accessibility problems (addon-a11y)
-
Add interactive controls for setting properties (Knobs)
-
Include inline documentation for each story (Docs)
-
Record snapshots of the HTML to test the impact of changes (Storyshots)
And do much more.
For further information about Storybook, see the website.
You can download the source for this recipe from the GitHub site.
1.10 Test Your Code in a Browser with Cypress
Problem
Most React projects include a testing library. The most common is probably @testing-library/react
, which comes bundled with
create-react-app
, or Enzyme, which is used by Preact.
But nothing quite beats testing code in a real browser, with all the additional complications that entails. Traditionally, browser testing can be unstable and requires frequent maintenance as you need to upgrade browser drivers (such as ChromeDriver) every time you upgrade the browser.
Add to that the issue of generating test data on a backend server, and browser-based testing can be complex to set up and manage.
Solution
The Cypress testing framework avoids many of the downsides of traditional browser testing. It runs in a browser but avoids the need for an external web-driver tool. Instead, it communicates directly with a browser, like Chrome or Electron, over a network port and then injects JavaScript to run much of the test code.
Let’s create an application create-react-app
to see how it works:
$
npxcreate-react-app
--use-npm
my-app
Now let’s go into the app directory and install Cypress:
$
cd
my-app
$
npminstall
cypress
--save-dev
Before we run Cypress, we need to configure it so that it knows how to find our application. We can do this by creating a cypress.json file in the application directory and telling it the uniform resource locator (URL) of our app:
{
"baseUrl"
:
"http://localhost:3000/"
}
Once we have started the main application:
$
npmstart
we can then open Cypress:
$
npxcypress
open
The first time you run Cypress, it will install all the dependencies it needs. We’ll now create a test in the cypress/integration directory called screenshot.js, which opens the home page and takes a screenshot:
describe
(
'screenshot'
,
()
=>
{
it
(
'should be able to take a screenshot'
,
()
=>
{
cy
.
visit
(
'/'
);
cy
.
screenshot
(
'frontpage'
);
});
});
You’ll notice that we wrote the tests in Jest format. Once you save the test, it will appear in the main Cypress window, shown in Figure 1-13.
If you double-click the test, Cypress will run it in a browser. The front page of the application will open, and the test will save a screenshot to cypress/screenshots/screenshot.js/frontpage.png.
Discussion
Here are some example commands you can perform with Cypress:
Command | Description |
---|---|
|
Finds the element containing |
|
Clicks the element with class |
|
Types |
|
Scrolls the |
These are just some of the commands that interact with the web page. But Cypress has another trick up its sleeve. Cypress can also
modify the code inside the browser to change the time (cy.clock()
), the cookies (cy.setCookie()
), the local storage
(cy.clearLocalStorage()
) and—most impressively—fake requests and responses to an API server.
It does this by modifying the networking functions that are built into the browser so that this code:
cy
.
route
(
"/api/server?*"
,
[{
some
:
'Data'
}])
will intercept any requests to a server endpoint beginning /api/server? and return the JSON array
[{some: 'Data'}]
.
Simulating network responses can completely change the way teams develop applications because it decouples the frontend development from the backend. The browser tests can specify what data they need without having to create a real server and database.
To learn more about Cypress, visit the documentation site.
You can download the source for this recipe from the GitHub site.
1 And yes, this means that Gatsby has TypeScript support built-in.
2 You can do this in most operating systems by pressing Ctrl-C.
3 The name is intentionally similar to create-react-app
. The maintainer of Razzle, Jared Palmer, lists create-react-app
as one of the inspirations for Razzle.
4 See Recipe 1.2.
6 For more information on creating Preact applications, see Recipe 1.5.
7 See the WHATWG specification for further details on custom elements and naming conventions.
8 For further details on shrinking Preact downloads, see Recipe 1.5.
Get React Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.