React's Ecosystem as a flexible Framework

React is only a view-layer library. Thus React only enables you to build component driven user interfaces. It comes with a couple of built-in solutions though, for instance local state management and syntactic events to make interactions happen, but after all you are only dealing with a view-layer library.

It is often said that plain React is sufficient when building applications. In the open source book the Road to learn React it is showcased that plain React suffices to build an application. But in the end, when implementing a larger application, you need a couple of more libraries to have a sophisticated web application with React as its core.

Developers coming from frameworks such as Angular or Ember often have a hard time to figure out all the building blocks they will need to build a sophisticated web application with React as its core. Coming from a framework, you are used to have all necessary functionalities at your disposal. However, React is only a view-layer library. Thus you would need to figure out all the other building blocks, to be more specific: all the other libraries that are needed to complement React. Nevertheless I think it is one of the crucial advantages of React in staying flexible when choosing your libraries to complement your React application.

I made this experience myself when I came from Angular to React. It might help you to understand the reasons behind a change from a framework to a library.

I would argue that React with its ecosystem is a flexible framework. You can choose your libraries to complement your React core. The following article will give you an opinionated approach to select from these libraries to build a sophisticated React application. In the end, you will have an opinionated list of building blocks. Nevertheless, it is up to you to exchange them with your own preferred libraries. After all, the article attempts to give newcomers in the React ecosystem an opinionated overview.

React's Boilerplate Decision

Even nowadays developers struggle on making a decision on how to setup their React project when joining the React community. There are thousands of boilerplate projects to choose from and every boilerplate project attempts to fulfil different needs. They vary in a range of minimalistic to almost bloated projects.

The status quo in the community is by starting your project with create-react-app. It comes with a zero-configuration setup and gives you a minimalistic up and running React application out of the box. You can always decide to lay open the toolchain by using its eject functionality. Afterward, you can alter the underlying toolchain.

In the end, there will never be the perfect boilerplate project. You will always have to add your own tooling. That’s why it makes sense, when having a solid understanding of React itself, to start of with a minimal React boilerplate project. You will be able to understand the underlying mechanics, you will do it on your own without copying a project, and you can add your own tooling to it. When choosing a bloated React boilerplate project in the first place, you will only be overwhelmed when you want to change something in the toolchain.

An alternative in the ecosystem, similar to create-react-app, is Next.js. It is a zero-configuration React application as well, but for server-side rendered React.

Recommendations:

  • create-react-app
  • Next.js for server-side rendered React
  • own minimal boilerplate, when having a solid understanding of React

Utility Libraries for React

JavaScript ES6 and beyond gives you tons of built-in functionalities dealing with arrays, objects, numbers, objects, strings etc. One of the most used JavaScript built-in functionalities in React is the map() method of arrays. Why? Because you always have to render a list of items in a component. Since JSX is a mixture of HTML and JavaScript, you can use JavaScript to map over your items and return JSX.

const List = ({ list }) =>
  <div>
    {list.map(item => <div key={item.id}>{item.title}</div>)}
  </div>

However, you might come to the point to choose a utility library that gives you more elaborated functionalities. You might even want to be more flexible when chaining these utility functions or even compose them dynamically into each other. That’s the point in time where you would introduce a utility library. My personal recommendations are two libraries.

The first recommendation is Lodash. It is the most widespread utility library in JavaScript. I guess there are people who know more about Lodash than about the native JavaScript functionalities, because people often learn libraries before learning a programming language, but also because JavaScript introduced new functionalities in its recent versions. Nevertheless, Lodash comes with a powerful set of functions to access, manipulate and compose.

The second recommendation is Ramda. When you lean towards functional programming (FP) in JavaScript, there is no way around this utility library. Even though Lodash comes with its own functional programming derivate (Lodash FP), I would always recommend using Ramda when dealing with FP in JavaScript. It gives you a powerful set of functionalities to be productive.

So when introducing a utility library to your React core, you could make the decision between Lodash and Ramda. Whereas Lodash is the more down to earth library for every JavaScript developer, Ramda comes with a powerful core when functional programming comes into play.

Recommendations:

  • JavaScript ES6 and beyond
  • Lodash
  • Ramda, when doing functional programming

Styling in React

When it comes to styling in React, it becomes opinionated in the React ecosystem. Not only regarding the specific solutions that are already out there, but because of the overarching philosophies. For instance, is it okay to have inline style in JSX? Is it fine to colocate style with components?

When starting with React, it is just fine to use plain CSS. If your first project is setup with create-react-app, you will encounter only CSS and can decide to add inline style too.

const Headline = ({ children }) =>
  <h1 className="headline" style={{ color: 'lightblue' }}>
    {children}
  </h1>

In smaller applications, it can be just fine to go only with plain CSS and inline style. Once your application scales, I would advice you to have a look into CSS modules. It gives you a way to encapsulate your CSS so that it doesn’t leak to other parts of the application. Parts of your application can still share style while other parts don’t have to get access to it. CSS modules scale well in growing applications. In React these modules are most often colocated files to your React component files.

A different approach of styling a component in React is defining a Styled Component. This approach is brought to you by a library called styled-components. It colocates styling in your JavaScript to your React components and doesn’t attempt to share the styling with other components. It only styles a specific component.

Last but not least, there is one neat helper library for styling in React: classnames. It enables you introducing conditional styling. In plain JavaScript, it would be possible to create a React class attribute with conditionals:

const Box = ({ status, children }) => {
  let classNames = ['box'];

  if (status === 'INFO') {
    classNames.push('box-info');
  }

  if (status === 'WARNING') {
    classNames.push('box-warning');
  }

  if (status === 'ERROR') {
    classNames.push('box-error');
  }

  return (
    <div className={classNames.join(' ')}>
      {children}
    </div>
  );
}

But it is so much easier with the classnames library:

import cs from 'classnames';

const Box = ({ status, children }) => {
  let classNames = cs('box', {
    'box-info': status === 'INFO',
    'box-warning': status === 'WARNING',
    'box-error': status === 'ERROR',
  });

  return (
    <div className={classNames}>
      {children}
    </div>
  );
}

It works perfectly with CSS modules too.

import cs from 'classnames';
import styles from './style.css';

const Box = ({ status, children }) => {
  let classNames = cs('box', {
    [styles.box_info]: status === 'INFO',
    [styles.box_warning]: status === 'WARNING',
    [styles.box_error]: status === 'ERROR',
  });

  return (
    <div className={classNames}>
      {children}
    </div>
  );
}

The library is almost mandatory in React applications when it comes to conditional stylings.

Recommendations:

  • plain CSS and inline style
  • CSS modules or Styled Components
  • almost mandatory classnames library

Asynchronous Requests in React

Beyond a Todo application in React, you will pretty soon have to make a request to a third party API. In the past, you would have often used jQuery for this kind of job.

Nowadays, recent browsers implement the native fetch API to conduct asynchronous requests. It uses promises under the hood. Basically a fetch looks like the following, for instance in a React lifecycle method when a component mounts:

componentDidMount() {
  fetch(my/api/domain)
    .then(response => response.json())
    .then(result => {
      // do success handling
      // e.g. store in local state
    });
}

Basically you wouldn’t have to add any other library to do the job. However, there exist libraries which only purpose it is to provide sophisticated asynchronous requests. They come with more powerful functionalities yet are only a lightweight library. One of these libraries that I would recommend is called axios. It can be used instead of the native fetch API when your application grows in size.

Recommendations:

  • native fetch API
  • axios

React's Higher Order Components

Eventually you get to the point where you want to abstract away functionalities for your components. These opt-in functionalities can be shared across components yet leave the components themselves lightweight. That’s when React’s higher order components come into play. These kind of components don’t need any additional library in React.

However, there are common use cases for React’s higher order components that are already solved in a library called recompose. For instance, having a higher order component for conditional rendering (branch). When you introduce higher order components to your React application, make sure that the use case is not already covered in recompose.

Another neat helper in the recompose library is the compose() function. It allows you to opt-in multiple higher order components in an elegant way. However, you could use a utility library such as Lodash or Ramda for the compose function too.

Recommendations:

  • recompose for utility higher order components
  • recompose or utility library (Lodash, Ramda) for compose

Type Checking

Fortunately React comes with its own type checking abilities. With PropTypes you are able to define the incoming props for your React components.

import PropTypes from 'prop-types';

const List = ({ list }) =>
  <div>
    {list.map(item => <div key={item.id}>{item.title}</div>)}
  </div>

List.propTypes = {
  list: PropTypes.array.isRequired,
};

Whenever a wrong type is passed to the component, you will get an error message when running the application.

However, in scaling React applications you can add sophisticated type checker such as Flow and TypeScript. When using such a type checker, you can get errors already during development time. You wouldn’t have to start your application in order to find about a bug that could have prevented with such type checking. That way a type checker might be able to improve your developer experience and avoids to introduce bugs in the first place.

Flow was introduced by Facebook and feels more natural in the React ecosystem than TypeScript. That’s why I recommend using it in a React application over TypeScript.

Recommendations:

  • React’s PropTypes
  • Flow (or TypeScript)

Formatting in React

Basically there are three options to have formatting rules in React. It should be quite similar to other ecosystems.

The first approach is to follow a style guide that is embraced by the community. One popular React style guide was open sourced by Airbnb. Even though you don’t deliberately follow the style guide, it makes sense to read it once to get the basics of formatting in React.

The second approach is to use a linter such as ESLint. You can integrate it in your toolchain when you are at the point of introducing new toolings to your project yourself.

The third and most important approach is using Prettier. It is an opinionated code formatter. You can integrate it in your editor or IDE that it formats your code every time you save a file or commit it with git. Perhaps it doesn’t match always your taste, but at least you never need to worry again about code formatting in your own or a team code base.

Recommendations:

  • reading one popular React style guide
  • Prettier

State Management

Fortunately React comes with its own local state management in components. This is why it is just fine to learn plain React first. You will only master the fundamentals in React when using this.state and this.setState() for local state management. Often newcomers make the mistake to learn React altogether with Redux. So don’t bother too early with a state management library when you are just starting to use React.

But what comes when you run into first scaling issues in React’s local state management? There are two solutions you can choose from: Redux and MobX. Both come with their advantages and disadvantages. You can read the linked article to make a more informed decision about them.

Redux is so popular yet such an innovative place that it comes with its own ecosystem. When using it with React, you will certainly run into the bridging library react-redux. It is the official library to connect your view layer (React) to your state layer (Redux). A similar library comes into play when you decide to use MobX instead of Redux.

As mentioned, Redux comes with its own ecosystem. The next recommendations are far beyond a simple setup for a React application. But when you scaled your application to a certain point, where Redux becomes an inherent part for your application and you are confident in using Redux, I can recommend to have a look into these libraries: Redux Saga, Normalizr and Reselect. I am releasing soon a book about state management in React where these topics are taught. You can subscribe to get to know when its released.

Recommendations:

  • React’s local state
  • Redux or MobX
    • when doing great with React’s local state
    • when needed

React's Routing

Routing is often introduced in an early stage in React applications. After all, React helps you implementing a view-layer that is most often used in a single page application. Thus routing is a crucial part of the application.

But before you introduce a heavy router in your application, when you are just about to learn React, you can give React’s conditional rendering a shot first. It is not a valid replacement for routing, but in small applications it is often sufficient to exchange components that way. It doesn’t change the URL though, but still you would be able to map different states to your view.

When introducing a sophisticated router, there are a few routing solutions out there for React. But the most anticipated solution is React Router. It works well along with an external state management library such as Redux or MobX too.

Recommendations:

  • React’s conditional rendering
  • React Router

So in the end, the React ecosystem can be seen as a framework for React, but it stays flexible. It is a flexible framework where you can make own decisions on which libraries you want to opt-in. You can start small and add only libraries to solve specific problems for you. You can scale your building blocks along the way when your application grows. Otherwise you can stay lightweight by using plain React. Therefore here again a list of libraries that could complement React as the core of the application regarding different project sizes. Keep in mind that the list is opinionated, but I am keen to get your feedback too.

  • Small Application
    • Boilerplate: create-react-app
    • Utility: JavaScript ES6 and beyond
    • Styling: plain CSS and inline style
    • Asynchronous Requests: fetch
    • Higher Order Components: optional
    • Formatting: none
    • Type Checking: none
    • State Management: local state
    • Routing: none or conditional rendering
  • Medium Application
    • Boilerplate: create-react-app with eject
    • Utility: JavaScript ES6 + Lodash or Ramda
    • Styling: CSS modules or Styled Components
    • Asynchronous Requests: fetch or axios
    • Higher Order Components: maybe + optional recompose
    • Formatting: Prettier
    • Type Checking: none or Flow
    • State Management: local state and very optional Redux
    • Routing: React Router
  • Large Application
    • Boilerplate: create-react-app with eject or own boilerplate project
    • Utility: JavaScript ES6 + Lodash or Ramda
    • Styling: CSS modules or Styled Components
    • Asynchronous Requests: axios
    • Higher Order Components: maybe + optional recompose
    • Formatting: Prettier
    • Type Checking: Flow
    • State Management: local state and Redux or MobX
    • Routing: React Router

The previous recommendations are opinionated. You can choose your own flexible framework for your ideal React application. Every “ideal” React setup is subjective to its needs of the developers and project. After all, there is no ideal React application setup.

Build a Hacker News App along the way. No setup configuration. No tooling. No Redux. Plain React in 170+ pages of learning material. Learn React like 10.000+ readers.

Get the Book
comments powered by Disqus

Never miss an article about web development and self-growth.

Take Part

Join 6400+ Developers

Learn Web Development

Tips and Tricks

Access Tutorials, Books and Courses

Personal Development as a Software Engineer