How to power a beautiful React UI with your favorite APIs

Meeshkan ML
7 min readMay 27, 2019

--

Designing and building a beautiful web UI has never been easier — in 2019, there exists a rich ecosystem covering almost every step of the process, from component libraries to packaging tools.

There is, however, one oft-ignored aspect in the process: that moment where your carefully-crafted components are hooked to the data coming from an external API. There are many things that can go wrong here: missing fields can cause elements to go awry, too long texts can cause collisions and overflow, and variable array sizes result in flex-box catastrophes. Also, setting up mock data for development is time-consuming and prone to errors.

In this article, we want to show how to start with rich JSON data from an API and build the components around the data, making sure that they accommodate different possible corner cases and layouts.

We will build a design app powered by Behance, a platform for creative folks to showcase their work. We would like data from Behance to drive our app and its development, but Behance API has pretty strict rate-limiting that makes development awkward. To get around this, we’ll use unmock to capture HTTP calls to Behance and receive mock data instead. The main use-case for unmock is to mock API calls in tests, but we can use it here as a convenient development tool.

While it is possible to use unmock without signing up, it is highly recommended to do so at https://unmock.io. It’s free and using your own unmock token ensures that your mocks are not visible to other users.

In order to make the Behance API come to life, we will use Grommet, a React framework providing “accessibility, modularity, responsiveness, and theming” for UIs. It includes powerful theming tools, comes with CSS Flexbox and Grid to provide layouts for all devices, and even has its own icons.

The full code with instructions is available in GitHub.

Project setup

We use Next.js to build our example app. Next.js is a React framework that adds file-system routing, automatic code splitting, and server-side rendering to a React app. That’s a mouthful, but suffice it to say that we love Next.js for its flexibility and performance. You can even export your Next.js app as a static site, which is perfect for light-weight use-cases like landing sites, blogs, or documentation. If you’ve never used Next.js before, don’t worry: we’ll cover the basics along the way.

Let’s take a look at our project’s package.json:

In the project dependencies, we include the expected stuff: Grommet for style, React and Next.js, unmock-node and unmock-jsdom to capture HTTP calls in the server and browser, and axios to create HTTP calls. @zeit/next-typescript plugin is used along with next/babel plugin to compile TypeScript and Next.js into JavaScript and HTML. These plugins are listed in .babelrc.

In the “scripts “ object of package.json, we define command-line scripts used for development. Running yarn startin the terminal (or npm start if you prefer to use npm) starts the local development server with hot module reloading.

If you signed up for unmock, you should define UNMOCK_TOKEN as an environment variable by, for example, starting your app with

UNMOCK_TOKEN={YOUR_TOKEN_HERE} yarn start

In next.config.js, we customize the webpack configuration with EnvironmentPlugin to ensure that UNMOCK_TOKEN is available also in the client code. Remember to remove the token from any client-side code if you ever decide to publish your app!

Finally in package.json, we mark unmock-node to be excluded from the browser build with browser: { unmock-node: false }. You can read more about including and excluding modules from the client or the server code in this article.

We won’t go deeper into the project setup here, but you can take a look at tsconfig.json (compiler options for TypeScript) and next.config.js (Next.js configuration) in GitHub.

Building our page

Next.js uses file-system routing to serve pages. This means that pages are served from pages/ folder with the names of the files corresponding to their routes. Our app only contains a single page served from the root of our app, so we’ll name the file index.tsx. Here’s the code for our page:

This file exports a React class named MainComponent decorated with the static getInitialProps method. This function is a Next.js feature used to populate the component properties in the server when the page is first loaded. We use it to download Behance projects with the getProjectsWithComments function. To be served back mock data instead of real Behance data, we also initialize unmock-node here.

The componentDidMount method of MainComponent is invoked in the client code immediately after the component is mounted in the DOM tree. We require and initialize unmock-jsdom here to capture outgoing HTTP calls in the browser. We do not want to include unmock-jsdom in our server build, so we import the library inside componentDidMount. If you’re interested to know more about client- and server-only modules in Next.js, we refer to this article.

The render() method of our MainComponent checks the err property to see if everything went smoothly when fetching data. If there’s no error, the component renders ProjectsGrid component that we’ll go through later. In case of an error, it renders the simple ErrorComponent.

Introducing the Grommet theme

To add a common theme to our app with Grommet, we’ll customize the Next.js App component in _app.tsx file. Appcomponent is used by Next.js to initialize all pages, so it’s a perfect place to define a custom theme and layout shared by all pages. Our _app.tsx file exports a React class component and looks like this:

We first define our Grommet theme. The theme sets the so-called base theme for all child components wrapped in the <Grommet theme={theme}> component in the render() method below.

We also define an AppBar component and include it in the render()method to have it show up at the top of every page in our app. AppBarand child component view are rendered in Grommet Box components. Box is a Grommet container that provides, for example, CSS flexbox capabilities for layout.

Fetching data from Behance

The code for fetching data from Behance is in util/behance.ts. The file defines two functions, getProjectsWithComments and getProject:

The getProjectsWithComments function first calls Behance API at /v2/projects endpoint to load recent projects. According to the API documentation, the response contains the field projects, which we store to the projects variable. Here’s a sample mock project returned by unmock:

Mock Behance project returned by unmock

Comments for each project are downloaded from /v2/projects/{projectId}/commentsendpoint with projects.map. The list of promises is awaited in Promise.all and the projects and their comments are returned in an array of objects.

The getProject function is used in our client components below to download project details when the user clicks a button. The decision to fetch project comments in server-side and project details in the client-side is done here for the sake of demonstration: you should decide which approach works best for your app.

Project grid

Remember how we included <ProjectsGrid> component in our index.tsx? Here’s the code for the component, excluding imports and type definitions:

The exported ProjectsGrid is a pure function component that takes props as argument and returns a template with Grid as outer-most element. Inside the grid, we add a ProjectBox component for each project. We’ll go and see the code for ProjectBox soon, but let’s first talk about state.

Each ProjectBox contains two buttons for interacting with the UI state. For example, when the user clicks the “Description” button, we want to open a modal overlay that shows the details of the clicked project.

Modal overlay for project description

Showing the modal overlays is triggered by the onOpenDescription and onOpenComments functions. These functions interact with the view via state, which we manage with React hooks. For example, for managing the state of the project description dialog, the relevant code is as follows:

When the onOpenDescription(projectId) is called (by the user clicking the “Description” button inside ProjectBox), we download project details from Behance API and set the data to the projectDetailsOpenForId state variable. Remember that we use unmock to capture the calls.

As the projectDetailsForOpenId variable becomes defined, the component renders DescriptionModal component and sets the project details now stored to the state as component properties. Also, the closeDescription function is injected to the child component so that the dialog can itself by resetting the state when the user clicks outside the dialog.

DescriptionModal component looks like this:

The modal overlay uses Grommet’s Layer component to define an overlay anchored to the center of the window. When the user clicks outside of the window or presses the escape key, props.close is called to close the dialog.

The code for comments-open-modal.tsx is similar, with the exception that comments for each project are fetched in the server-side when loading projects. With this strategy, initial page load takes a bit longer but the comment overlay opens instantly when the user clicks the button. You may want to mix these two strategies (eager and lazy data loading) in your own application depending on the UX you’re after.

Finally, ProjectBox is defined as follows:

As the name implies, the component lives in a Box. Inside each box, we include a heading, project cover image, and the buttons for opening project details and comments.

Conclusion

That’s it for our app, we covered a lot! Your final app should look like the following.

Our app

If you want to dive deeper, you could add, for example,

  • more pages and navigation buttons
  • client-side navigation between the pages with Link
  • server-side rendering of styled-components (used by Grommet)

Please leave feedback or questions in the comments below! If you’re interested in building Dev Tools 2.0 with us, check out our open positions in The Hub or contact us with an open application. Thanks for reading!

--

--

Meeshkan ML

Meeshkan Machine Learning is a machine learning company based in Helsinki, Finland. We’re hiring! https://thehub.fi/jobs/company/meeshkan