This website uses cookies to better the user experience of its visitors. Where applicable, this website uses a cookie control system, allowing users to allow or disallow the use of cookies on their computer/device on their first visit to the website. This complies with recent legislative requirements for websites to obtain explicit consent from users before leaving behind or reading files such as cookies on a user’s computer/device. To learn more click Cookie Policy.

Privacy preference center

Cookies are small files saved to a user’s computer/device hard drive that track, save, and store information about the user’s interactions and website use. They allow a website, through its server, to provide users with a tailored experience within the site. Users are advised to take necessary steps within their web browser security settings to block all cookies from this website and its external serving vendors if they wish to deny the use and saving of cookies from this website to their computer’s/device’s hard drive. To learn more click Cookie Policy.

Manage consent preferences

These cookies are necessary for the website to function and cannot be switched off in our systems. They are usually only set in response to actions made by you which amount to a request for services, such as setting your privacy preferences, logging in or filling in forms. You can set your browser to block or alert you about these cookies, but some parts of the site will not then work. These cookies do not store any personally identifiable information.
These cookies allow us to count visits and traffic sources so we can measure and improve the performance of our site. They help us to know which pages are the most and least popular and see how visitors move around the site. If you do not allow these cookies we will not know when you have visited our site, and will not be able to monitor its performance.
Cookies list
Name _rg_session
Provider rubygarage.org
Retention period 2 days
Type First party
Category Necessary
Description The website session cookie is set by the server to maintain the user's session state across different pages of the website. This cookie is essential for functionalities such as login persistence, ensuring a seamless and consistent user experience. The session cookie does not store personal data and is typically deleted when the browser is closed, enhancing privacy and security.
Name m
Provider m.stripe.com
Retention period 1 year 1 month
Type Third party
Category Necessary
Description The m cookie is set by Stripe and is used to help assess the risk associated with attempted transactions on the website. This cookie plays a critical role in fraud detection by identifying and analyzing patterns of behavior to distinguish between legitimate users and potentially fraudulent activity. It enhances the security of online transactions, ensuring that only authorized payments are processed while minimizing the risk of fraud.
Name __cf_bm
Provider .pipedrive.com
Retention period 1 hour
Type Third party
Category Necessary
Description The __cf_bm cookie is set by Cloudflare to support Cloudflare Bot Management. This cookie helps to identify and filter requests from bots, enhancing the security and performance of the website. By distinguishing between legitimate users and automated traffic, it ensures that the site remains protected from malicious bots and potential attacks. This functionality is crucial for maintaining the integrity and reliability of the site's operations.
Name _GRECAPTCHA
Provider .recaptcha.net
Retention period 6 months
Type Third party
Category Necessary
Description The _GRECAPTCHA cookie is set by Google reCAPTCHA to ensure that interactions with the website are from legitimate human users and not automated bots. This cookie helps protect forms, login pages, and other interactive elements from spam and abuse by analyzing user behavior. It is essential for the proper functioning of reCAPTCHA, providing a critical layer of security to maintain the integrity and reliability of the site's interactive features.
Name __cf_bm
Provider .calendly.com
Retention period 30 minutes
Type Third party
Category Necessary
Description The __cf_bm cookie is set by Cloudflare to distinguish between humans and bots. This cookie is beneficial for the website as it helps in making valid reports on the use of the website. By identifying and managing automated traffic, it ensures that analytics and performance metrics accurately reflect human user interactions, thereby enhancing site security and performance.
Name __cfruid
Provider .calendly.com
Retention period During session
Type Third party
Category Necessary
Description The __cfruid cookie is associated with websites using Cloudflare services. This cookie is used to identify trusted web traffic and enhance security. It helps Cloudflare manage and filter legitimate traffic from potentially harmful requests, thereby protecting the website from malicious activities such as DDoS attacks and ensuring reliable performance for genuine users.
Name OptanonConsent
Provider .calendly.com
Retention period 1 year
Type Third party
Category Necessary
Description The OptanonConsent cookie determines whether the visitor has accepted the cookie consent box, ensuring that the consent box will not be presented again upon re-entry to the site. This cookie helps maintain the user's consent preferences and compliance with privacy regulations by storing information about the categories of cookies the user has consented to and preventing unnecessary repetition of consent requests.
Name OptanonAlertBoxClosed
Provider .calendly.com
Retention period 1 year
Type Third party
Category Necessary
Description The OptanonAlertBoxClosed cookie is set after visitors have seen a cookie information notice and, in some cases, only when they actively close the notice. It ensures that the cookie consent message is not shown again to the user, enhancing the user experience by preventing repetitive notifications. This cookie helps manage user preferences and ensures compliance with privacy regulations by recording when the notice has been acknowledged.
Name referrer_user_id
Provider .calendly.com
Retention period 14 days
Type Third party
Category Necessary
Description The referrer_user_id cookie is set by Calendly to support the booking functionality on the website. This cookie helps track the source of referrals to the booking page, enabling Calendly to attribute bookings accurately and enhance the user experience by streamlining the scheduling process. It assists in managing user sessions and preferences during the booking workflow, ensuring efficient and reliable operation.
Name _calendly_session
Provider .calendly.com
Retention period 21 days
Type Third party
Category Necessary
Description The _calendly_session cookie is set by Calendly, a meeting scheduling tool, to enable the meeting scheduler to function within the website. This cookie facilitates the scheduling process by maintaining session information, allowing visitors to book meetings and add events to their calendars seamlessly. It ensures that the scheduling workflow operates smoothly, providing a consistent and reliable user experience.
Name _gat_UA-*
Provider rubygarage.org
Retention period 1 minute
Type First party
Category Analytics
Description The _gat_UA-* cookie is a pattern type cookie set by Google Analytics, where the pattern element in the name contains the unique identity number of the Google Analytics account or website it relates to. This cookie is a variation of the _gat cookie and is used to throttle the request rate, limiting the amount of data collected by Google Analytics on high traffic websites. It helps manage the volume of data recorded, ensuring efficient performance and accurate analytics reporting.
Name _ga
Provider rubygarage.org
Retention period 1 year 1 month 4 days
Type First party
Category Analytics
Description The _ga cookie is set by Google Analytics to calculate visitor, session, and campaign data for the site's analytics reports. It helps track how users interact with the website, providing insights into site usage and performance.
Name _ga_*
Provider rubygarage.org
Retention period 1 year 1 month 4 days
Type First party
Category Analytics
Description The _ga_* cookie is set by Google Analytics to store and count page views on the website. This cookie helps track the number of visits and interactions with the website, providing valuable data for performance and user behavior analysis. It belongs to the analytics category and plays a crucial role in generating detailed usage reports for site optimization.
Name _gid
Provider rubygarage.org
Retention period 1 day
Type First party
Category Analytics
Description The _gid cookie is set by Google Analytics to store information about how visitors use a website and to create an analytics report on the website's performance. This cookie collects data on visitor behavior, including pages visited, duration of the visit, and interactions with the website, helping site owners understand and improve user experience. It is part of the analytics category and typically expires after 24 hours.
Name _dc_gtm_UA-*
Provider rubygarage.org
Retention period 1 minute
Type First party
Category Analytics
Description The _dc_gtm_UA-* cookie is set by Google Analytics to help load the Google Analytics script tag via Google Tag Manager. This cookie facilitates the efficient loading of analytics tools, ensuring that data on user behavior and website performance is accurately collected and reported. It is categorized under analytics and assists in the seamless integration and functioning of Google Analytics on the website.

How to Build a ReactJS SPA using Apollo Client, NextJS, Storybook and Material-UI

  • 92748 views
  • 18 min
  • Nov 01, 2018
Aleksey B.

Aleksey B.

Ruby/JS Developer

Daria R.

Daria R.

Copywriter

Share

Modern times demand modern solutions. That’s why as a developer you need to know how to build one the most in-demand types of projects nowadays ‒ a single-page web app, or SPA. Their popularity among product owners can be explained by the fact that such apps don’t load every new page but only pull data from a server. This approach leads to shorter response times and a better customer experience. Another great plus of SPAs is their effectiveness, as they allow developers to reuse code within the app.

React as a library to build single-page applications

For today’s tutorial (and for our own projects), we use ReactJS to build top-notch single-page applications. This choice is based on React’s long list of advantages. Here are the most significant of them.

  • Reusable components. React is a component library. By using the same components multiple times, you save time and create easy-to-maintain, bug-free code.
  • Virtual DOM. A browser’s DOM is slow. Using a virtual DOM, when you make changes in your data the real DOM tree is compared to the virtual DOM, and then it tracks the differences and only redraws changed elements.
  • Open source library. ReactJS is a free open source tool, which means it’s developed not only by Facebook programmers but also with the help of thousands of enthusiasts. Many of them have made significant contributions to advance the library, helping to put it in seventh place in the trending list on GitHub.
  • Server-side rendering. Server-side rendering can help you avoid a lot of issues with browser compatibility and improves web indexing.
  • Easy to test. React is well-designed for testing and debugging. Using one of numerous external libraries (Jest, in our case), you can get all the functionality you need out of the box.
  • Improved readability/maintainability. The React library is based on a component hierarchy that can be reused numerous times, making code readable and improving the maintainability of a web app.
  • Performance. React JS has a bunch of features and peculiarities which make it super productive and efficient, such as the virtual DOM, inline functions, and a component structure.
  • Developer tool sets. The React tool set lets developers inspect hierarchies of React components in the virtual DOM. These tool sets allow you to choose separate components and check their properties and states. They also help to improve the debugging process.
  • Maintained by Facebook. React gets constant support from Facebook developers, who improve it all the time.
  • One-way data flow. By making React JS just a view system, developers have managed to provide the ability to change child structures without affecting their parents. This makes code more stable.
  • Redux compatibility. While applying React to your project, it’s very easy to use Redux as they’re compatible with each other. This compatibility makes a developer’s life easier.

Building a single-page application with React

The task

As an example for our tutorial, we decided to create a GitHub client based on React JS using the GitHub GraphQL API for interactions. The main features of this client will be search for top repos and new repos.

Below, you can see a list of technologies that we’ve chosen for the project and the reasons why we prefer them.

Technology stack

ReactJS — A declarative and component-based library for creating interactive UIs, React is able to update and render components when data changes.

GraphQL — This query language for working with APIs allows us to get a comprehensive description of data in an API and makes it easier to get exactly the data we need in our requests.

Apollo-client — We can drop this adoptable tool into our JavaScript application and begin using GraphQL for all or part of the app UI. Apollo is an incredibly compatible, understandable, and flexible instrument that’s easy to get started with.

NextJS 6 — This lightweight framework for server-rendered ReactJS applications focuses on pre-rendering to improve performance.

Babel — This handy JavaScript compiler is necessary because of slow browser adoption of the latest JavaScript language features. Babel has plugins that allow us to use all the newest JavaScript features even if they don’t have browser support yet.

ESLint — We use this tool to check code quality. Apart from checking general style, ESLint helps us to find certain classes of bugs, such as those related to variable scope.

Jest + Enzyme — This combination of instruments makes the testing process much more pleasant. Jest is very fast, can run Snapshot testing, has really helpful fail messages, re-runs only tests that are relevant to changes that were made, and is simple to configure. Enzyme is a useful testing utility for React.

Yarn — An ultra-fast package manager, we like Yarn for its speed, security, and reliability.

Storybook — A development environment for UI components, Storybook allows us to browse component libraries, view the different states of each component, and interactively develop and test components.

Material-UI — These are the React components that help us implement Google Material Design.

Atomic design — This is a methodology for creating design component systems instead of creating pages.

Step-by-step tutorial

To create a single-page GitHub client based on React JS using the GitHub GraphQL API for interactions, follow the steps below.

Step 1 — Install Yarn

There are two ways to install Yarn. The first option is to use npm:

Another option is to go to the official download page, get the installer for your operating system, and run it.

Step 2 — Initialize the project

To start, you need to create a simple project by running the following commands:

Then open package.json in the github-client directory and add the following script:

Now everything is ready. To start the dev server, run the following command:

When you run localhost:3000 in your browser, you’ll see the error page

Step 3 — Install Babel

Babel is a toolchain that’s mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript for older browsers or environments.

To install the Babel compiler core, use this command:

Babel is a compiler that accepts source code and renders output code. Like many other compilers, it runs in three stages: parse, transform, and print.

But out of the box, Babel doesn’t do anything useful. It basically acts like const babel = code => code, parsing the code and then generating the same code back out again. You’ll need to add plugins for Babel to do anything useful.

3.1 Install the Babel module resolver

When using Babel, you can add new root directories that contain modules and set up custom aliases for directories, specific files, or even other npm modules.

To install the Babel module resolver, use this command:

After that, update .babelrc config to import dependencies without declaring a related path.

This allows you to write this

Instead of this:

Step 4 — Configure linters

To avoid lots of refactoring in future, integrate linters in your app. To do that, add eslint as a development dependency:

You need a wrapper for Babel’s parser used for eslint.

Use Yarn to install this package:

Babel-eslint allows you to lint all valid Babel code.

There are also a few dependencies that you’ll have to install.

This package provides Airbnb’s .eslintrc as an extensible shared config.

Install the ESLint plugin with rules that help validate proper imports:

Install Static AST checker for accessibility rules on JSX elements:

Install the eslint plugin for React:

Initialize eslint config:

We’re going to use the Airbnb eslint config, so choose the following settings:

How would you like to configure ESLint? — Use a popular style guide

Which style guide do you want to follow? — Airbnb

Do you use React? — Yes

What format do you want your config file to be in? — JSON

Would you like to install them now with npm? — No

Now you should have an .eslintrc.json file with the following configuration:

We’re going to use .js extensions instead of .jsx because JSX isn’t standard JavaScript. So add this option to .eslintc.json:

Step 5 — Integrate Material-UI

In plain English, Material-UI is an open source project that features React components which implement Google’s Material Design.

There are a few additional steps that we need to take before we can start using Material-UI with the NextJS 6 framework.

First of all, we need to install some additional packages.

5.1 Install JSS

JSS is a more powerful abstraction than CSS. It uses JavaScript to describe styles in a declarative and maintainable way. It’s a high-performance JavaScript to CSS compiler that works at runtime and on the server side.

5.2 Install react-jss

We use react-jss because it provides components for JSS as an abstraction layer and has the following benefits compared to a lower-level core:

  • Theming support
  • Critical CSS extraction
  • Lazy evaluation — a sheet is created only when a component is mounted
  • Auto attach/detach — a sheet is rendered in the DOM when a component is about to mount, and is removed when no element needs it
  • Style sheets get shared between all elements
  • Function values and rules are updated automatically with props

5.3 Install the styled-jsx package

Styled-jsx is a full, scoped, and component-friendly CSS support for JSX that’s rendered on the server or the client.

5.4 Install Material-UI core and icon packages

@material-ui/core is a set of React components that implement Google Material Design.

@materail-ui/icons is a set of components with SVG icons.

If you want to use Material-UI in the Next.js framework, you need to configure JSS injection to render material styles properly (with server-side rendering) as shown in the official material-ui repo.

5.5 Now create getPageContext.js

Next.js uses the App component to initialize pages. But you can override it and control page initialization, allowing you to do amazing things like:

  • persist the layout between page changes;
  • maintain state when navigating pages;
  • implement custom error handling using the DidCatch component;
  • inject additional data into pages (for example, by processing GraphQL queries).

To override the App component, create a ./pages/_app.js file and override the App class as shown below:

Pages in Next.js skip the definition of the surrounding document’s markup. For example, you never include <html>, <body>, and so on. To override that default behavior, create a file at ./pages/_document.js where you can extend the Document class.

You need to use the codebase from the official material-ui repo example with Next.js:

Now we’re ready to implement some pages with components.

Step 6 — Storybook integration

Let’s integrate Storybook, a handy UI development environment. Here you can find a start guide for React.

First of all, you need to add @storybook/react to your project. To do that, simply run:

Then add the following script to your JSON package in order to start the Storybook later in this guide:

6.1 Create a config file.

A Storybook can be configured in several different ways. That’s why we need a config directory. We’ve added a -c option to the above script, mentioning .storybook as the config directory.

For the basic Storybook configuration file, simply tell Storybook where to find stories.

For that, create a file at .storybook/config.js with the following content:

Here we use Webpack’s require.context to load modules dynamically.

6.2 Add Babel plugin

This replicates Webpack require.context.

6.3 Update the Babel config

6.4 Initialize the require context hook in testConfig

All files with a .stories extension inside src/components will be required dynamically.

Create a separate Babel config for Storybook to avoid conflicts with different environments.

Adjust eslint config (so we don’t see warnings when importing Storybook packages):

Storybook is all about writing stories. Usually, a story contains a single state of one of your components, very similar to a visual test case.

In a nutshell, a story is a function that returns a React element.

Step 7 — Atomic design

Atomic design helps us build consistent, solid, and reusable design systems. More than that, in the world of React that stimulates componentization, atomic design is used unconsciously. But don’t forget that It can become a powerful tool only if you use it in the right way.

First of all, create an index.js entry point for the components directory:

Atomic design should be a solution, not another problem. If you want to create a component and don’t know where to put it (atoms, molecules, organisms, etc.), don’t worry; don’t think too much, just put it anywhere. After you realize what it is, just move the component folder to the right place. Everything else should work.

This is possible because all components are dynamically exported to components/index.js and imported so that the atomic design structure doesn’t matter:

Let’s create our first atom — a material button.

7.1 Create atoms

Atoms are the basic building blocks of matter. Applied to web interfaces, atoms are HTML tags, such as form labels, inputs, and buttons. They can also include more abstract elements like color palettes and fonts as well as more invisible aspects of an interface like animations.

Create a button atom

After that, write a story for this atom:

Now let’s run it:

We’re going to create a few more atoms using the same approach:

  • IconButton
  • AppBar
  • Card
  • CardActions
  • CardContent
  • List
  • ListItem
  • ListItemIcon
  • ListItemText
  • Loader
  • MenuIcon
  • SwipeableDrawer
  • Toolbar
  • Typography
  • Grid

7.2 Create molecules

Molecules are groups of atoms that are bonded together. They take on their own properties and serve as the backbone of our design systems.

Сreate SimpleCard, the first molecule component

After that, write a story for this molecule:

We’re going to create a few more molecules using the same approach:

  • Header
  • SwipeableMenu

7.3 Build organisms

Organisms are groups of molecules joined together to form a relatively complex, distinct section of an interface.

Building up from molecules to organisms encourages creating standalone, portable, reusable components.

Create a header with a swipeable menu organism

After that, write a story for this organism:

7.4 Create templates

Templates are page-level objects that place components into a layout and articulate the design’s underlying content structure.

Build the home template:

After that, write a story for this template

Step 8 — Create pages

Now we can use Next.js pages as an entry point.

The file system is the main API. Every .js file becomes a route that gets automatically processed and rendered. If we run yarn dev, we can access this page at localhost:3000.

Step 9 — Implementing authentication

To communicate with the GitHub GraphQL API, we need to create a GitHub application first. Follow this guide to create your GitHub app. This guide will help you to develop environment Homepage URL, and this one is necessary for the authorization callback URL.

To use GitHub secret keys, you’ll need to use the dotenv package.

Сreate next.config.js with following commands:

Now you need to add the GitHub app secret keys. We can use the .env file for storing secret API keys that will be available only on the server side.

Add this file to .gitignore.

9.1 Create containers

If we need to implement some component with its own state management or side effects (in other words, a smart component), we can place it in the containers folder. All components with GraphQL/REST requests will be there.

We can use GraphQL queries in our containers using the react-apollo query component.

9.2 GitHub login button container

We need a login button container to make a request to the GitHub authentication endpoint and redirect to the callback page.

9.3 Callback page

GitHub redirects to a callback URL on your website (which you provide when registering your app with GitHub).

We need to implement the callback page that will be used to obtain the access_token.

Install isomorphic-unfetch (Tiny 500b fetch "barely-polyfill"). We will use this package for authenticating REST requests only.

We need to store access_token on the client side using cookies. A simple, lightweight JavaScript API for handling cookies.

Now you need to implement callback page:

getInitialProps is an asynchronous static method. It can asynchronously fetch anything that resolves to a plain JavaScript object, which populates props.

Data returned from getInitialProps is serialized during server rendering, similar to JSON.stringify. Make sure the returned object from getInitialProps is a plain object and doesn’t use Date, Map, or Set.

For the initial page load, getInitialProps will execute on the server only. getInitialProps will only be executed on the client when navigating to a different route via the Link component or using the routing APIs.

Step 10 — Integrate GraphQL with Apollo

Apollo Client is the best way to use GraphQL to build client applications. The client is designed to help you quickly build a UI that fetches data with GraphQL, and can be used with any JavaScript frontend.

The easiest way to get started with Apollo Client is by using Apollo Boost. It’s a starter kit that configures your client with recommended settings.

Install the following packages:

  • apollo-boost — A package containing everything you need to set up the Apollo Client
  • react-apollo — A view layer integration for React
  • graphql — Also parses your GraphQL queries
  • graphql-tag — Provides a template literal tag that you can use to concisely write a GraphQL query that’s parsed into the standard GraphQL AST

Now that you have all the dependencies you need, let’s create your Apollo Client. The only thing you need to get started is the endpoint for your GraphQL server.

If you don’t pass the URI directly, it defaults to the /graphql endpoint on the same host your app is served from.

10.1 GraphQL authentication

Apollo Client uses a very flexible Apollo Link that includes several options for authentication.

We’ll use cookies for storing the GitHub access_token and send it as an authorization header. It’s easy to add an authorization header to every HTTP request by adding headers to Apollo Client.

It’s very easy to tell your network interface to send a cookie along with every request. You just need to pass the headers option, for instance headers: 'token'.

In this example, we’ll pull the login token from cookies every time a request is sent:

After that, we can make requests to the GitHub API using our token from cookies.

10.2 GraphQL queries

Let’s implement our first GraphQL query.

This query finds the last 50 repositories with more than 10,000 stars.

Now let’s implement SearchRepoList container

Now let’s implement the Next.js page:

Step 11 — Testing

Testing is one of the most important stages of development. It’s time to test everything that we’ve implemented so far.

11.1 Add Jest for testing

You need to add Jest as a development dependency:

11.2 Add enzyme

We need different Babel presets for the test environment, specifically for the Next.js app.

Enzyme expects an adapter to be configured:

11.3 Connect test config

Add to eslintrc.json.

This will add all Jest-related things to your environment, eliminating the linter errors/warnings.

Add to `package.json`:

This allows us to use yarn test to run all Jest specs.

We’ll add a simple test for our button component:

Now if we run yarn test, we should have one passed spec.

After that, we’ll add more simple components (atoms) which we’ll use for the home page.

11.4 Test card atom

11.5 Test SimpleCard molecules

11.6 Test HeaderWithSwipeableMenu organism

11.7 Test the home template

11.8 Create snapshot testing

Snapshot testing is a very useful tool whenever you want to make sure your UI doesn’t change unexpectedly.

To add react-test-render, write this command:

This package provides a React renderer that can be used to render React components to pure JavaScript objects without depending on the DOM or a native mobile environment.

Now let’s test callback page

You can build all other components using the templates above, or you can find the full codebase in our GitHub repository. You can also check out the demo app we built with this tutorial.

To get more tutorials on this topic, subscribe to our blog. And feel free to start a discussion in the comments below!

CONTENTS

Authors:

Aleksey B.

Aleksey B.

Ruby/JS Developer

Daria R.

Daria R.

Copywriter

Rate this article!

Nay
So-so
Not bad
Good
Wow
6 rating, average 4.83 out of 5

Share article with

Comments (2)
Rajneesh Ojha
Rajneesh Ojha over 5 years ago
Loved the article that much that i had to comment , i dont remember when last time i made a comment on an article
Reply
Anastasia Z.
Anastasia Z. over 5 years ago Rajneesh Ojha
Hi there! Thanks for your kind feedback ;)
Reply

Subscribe via email and know it all first!