DEV Community

Cover image for React Boilerplate: Everything you need to quick-start a new React project inside one repo
Mad Devs for Mad Devs

Posted on • Updated on

React Boilerplate: Everything you need to quick-start a new React project inside one repo

When creating a new React project, I faced the same problems every time. I need to build it manually because create-react-app do not provide everything that we use in real projects. We need to set up data storage, routing, configure REST, styles, etc. This was quite time-consuming, and our team decided to create some boilerplate to quick-start new projects. We’re starting to develop boilerplates for different use cases. In this article, I want to present my work to you: a boilerplate for a React application.

image

Technology Stack

Here’s a list of libraries and dependencies used for this boilerplate. We’d decided to use minimal dependencies, and you can add anything if you need.

  1. React-router-dom: Declarative routing for React.
  2. Styled-components: Use the best bits of ES6 and CSS to style your apps without stress. The best CSS-in-JS library for React.
  3. React-use: Some very simple and useful hooks for components that you can build amazing things with.
  4. Prop-types: Runtime type checking for React props and similar objects.
  5. Axios: The best promise-based HTTP client for Javascript.
  6. Jest: Delightful JavaScript Testing Framework with a focus on simplicity.
  7. Testing-library: Simple and complete testing utilities that encourage good testing practices.
  8. ESLint+Prettier: Linter to find and fix problems in your JavaScript code. Prettier for code formatting.

First start

Using docker and docker-compose

This option is good in that you don’t need to install a lot of dependencies on your working device. Docker just encapsulates all that trash.

To start the project with this option, you need to install Docker and docker-compose

Then, you just need to run the following command:

yarn docker:dev
When Docker installs all the necessary dependencies and builds your application, you will see Compiled successfully in your console. Your project is available on 3000 port; you can open it and start developing http://localhost:3000

Using npm

If you can’t or don’t want to use docker, you can use the default method for starting your project using Node.JS and npm(yarn)

  1. Install dependencies

yarn # or npm i

  1. Start the project

yarn start # or npm start

The application is available at http://localhost:3000

Routing

We use react-router-dom for routing in the application. All the routes are stored in the src/Router.jsx file.

import React from 'react'
import { Route, Switch } from 'react-router-dom'
// pages
import { Main, Todo } from './pages'
function Router() {
  return (
    <Switch>
      <Route exact path='/' component={Main} />
      <Route exact path='/todo' component={Todo} />
    </Switch>
  )
}

export default Router
Enter fullscreen mode Exit fullscreen mode

Adding a new route

To create a new route, you need to do the following steps:

1.Create the new file in the src/pages folder
2.Add the new created page in the src/pages/index.js file for better importing.
3.Add the new page in src/Router.jsx file

Additional information

  • Documentation
  • Important: The pages are used only to logically separate different parts of the application. You don’t need to use the pages as components. You can use react-helmet to set up the page’s meta-tags (title, description, etc.)

Components

When you work with the components, it’s recommended to use a modern approach with functional components and hooks.

It’s not recommended to use class components because they work too slowly (performance) and won’t be supported

To create the component, you can use the following CLI-script:

yarn create:component MyComponent

After using this script, the folder with your component’s name will appear in your src/components folder. In this case, it will be the src/components/MyComponent folder.

Component’s files description

index.jsx— core file with logic. Example:

import React, { useState } from 'react'
import { useMount, useUpdateEffect } from 'react-use'

// view
import TodoList from './TodoList'

function Wrapper() {
  const [todos, setTodos] = useState([])

  const getInitialTodos = () => {
    // ...some logic to get initialTodos from localStorage
  }

  const saveTodos = () => {
    // ...some logic to save todos from localStorage
  }

  const addTodo = todo => setTodos([...todos, todo])
  const removeTodo = todo => setTodos([...todos.filter(todo => todo === todo)]);

  // Use mount-hook for calling getInitialTodos() after mount
  useMount(() => getInitialTodos())

  // Watch todos and save it in localStorage after updating
  useUpdateEffect(() => saveTodos(), [todos])

  // return view with some props
  return <TodoList todos={todos} addTodo={addTodo} removeTodo={removeTodo} />
}

export default Wrapper
Enter fullscreen mode Exit fullscreen mode

[ComponentName].jsx — view file(markup). Example:

import React from 'react'
import PropTypes from 'prop-types'

import TodoItem from '../TodoItem'

// prop-types
const propTypes = {
  todos: PropTypes.arrayOf(PropTypes.object),
}

function TodoList({ todos }) {
  if(!Boolean(todos.length)) return <div className="empty">No todos :)</div>
  return (
    <div className="todo-list">
      {todos.map(todo => <TodoItem key={todo.id} todo={todo} />)}
    </div>
  )
}

TodoList.propTypes = propTypes

export default TodoList
Enter fullscreen mode Exit fullscreen mode

[ComponentName].test.jsx — unit tests

[ComponentName].styles.js— styles (styled-components by default)

Useful links

LocalStorage

To work with localStorage you can use additional utilities: loadState and saveState Example:

import { saveState, loadState }  from '../utils/localStorage'
const save = data => saveState(data, 'key')
const load = () => loadState('key')
Enter fullscreen mode Exit fullscreen mode

Hooks

To build logic within components, people usually use hooks.

React-use

This is a library of additional react hooks that meet most of the needs so that you do not have to reinvent the wheel every time. All hooks list

Most useful hooks:

  • useDebounce — for use debounce effect in the component
  • useLocalStorage — for working with localStorage in the component
  • useMount — mount lifecycle hook
  • useUpdateEffect — update lifecycle hook
  • usePrevious — store prevState or prevProps
  • useBoolean — simple state hook for boolean values
  • useList — state hook for store arrays with additional utilities

Custom hooks

Creating custom hooks is a very useful thing as it allows reusing large amounts of code. If you see code that will probably be reused in the future, hook it. This is an example of using a simple custom hook implementing work with API:

import { useState } from 'react'
import { useList, useToggle } from 'react-use'

import fetchImages from './fetchImages'

const useFetchImages = ({ source }) => {
    const [images, imagesActions] = useList([])
    const [isLoading, toggleLoading] = useToggle(false)
    const [error, setError] = useState(null)

    const fetchImages = async () => {
        toggleLoading(true)
        try {
            const data = await fetchImages(source)
            imagesActions.set(data)
        } catch(err) {
            setError(err)
        }
        toggleLoading(false)
    }

    return {
        images,
        isLoading,
        error,
        fetchImages,
    }
}

export default useFetchImages
Enter fullscreen mode Exit fullscreen mode

Utilities

Utilities are stored in the src/utils folder in separate files.

Available utilities

  • camelToSnakeCase and snakeToCamelCase— transformation of a string into various styles of writing phrases without spaces or punctuation
  • normalizeObjectKeys — transformation of all the object field keys using snakeToCamelCase
  • normalizeCollectionKeys — transformation of all the elements(Element should be an object) of the array using normalizeObjectKeys
  • getRequestParams — function for getting values of get-parameters from location.search
  • localStorage — utilities for working with localStorage

Axios

In working with API requests, the most useful library is axios.

Configuration

Axios configuration is in the src/config/api.js file.

Additional function setApiHeader

If you need to add a header in the existing axios instance, you can use setApiHeader function. Example:

import { setApiHeader, api } from '../config'

async function authenticate() {
  // Authorization
  const response = await api.post('/auth')

  // Getting token from response
  const { token } = response

  // Set header for the next authenticated requests
  setApiHeader('Authorization', `Bearer ${token}`)
}

authenticate()
Enter fullscreen mode Exit fullscreen mode

Always try to use async/await syntax.

Environment variables

To work with environment variables, we need to use some config files:

  • .env.example — for storing examples of variables
  • .env — for variables To add a new environment variable, you need to do the following steps:
  1. Add variables into .env.example file with empty value
REACT_APP_API_BASE_URL=
Enter fullscreen mode Exit fullscreen mode
  1. Add variable with value int .env file.
REACT_APP_API_BASE_URL=https://google.com/api
Enter fullscreen mode Exit fullscreen mode
  1. Restart the project (required)

  2. Add the variable into the config (src/config/index.js) and use it from config

export const config = {
  API_URL: process.env.REACT_APP_API_BASE_URL,
}

axios.get(config.API_URL)
Enter fullscreen mode Exit fullscreen mode

Don’t forget to restart the project after adding/updating any >variables

Environment variables should be ALWAYS started by REACT_APP_ ; otherwise, they won’t work

Styles

To write styles, we can use several approaches:

  • Scss/BEM — default styling
  • Css-In-JS (styled-components) — a recommended option that is simpler and more convenient than the previous one.

Testing

In testing everything, Jest and React-testing-library are used.

Useful links

Unit-tests running

There are several scripts to run tests:

  • yarn test — watch-mode
  • yarn test:coverage — watch-mode+coverage
  • yarn test:ci — without watch-mode + coverage + disable coloring output

Coverage

Coverage generates after running yarn test:coverage command. You can see expanded coverage in the HTML format in the ./coverage folder.

Unit tests also have a minimal coverage threshold. If coverage is less than 80%, the tests will fail

Formatting

Linters are to keep code clean. They prevent shitcode from getting into a repository.

ESLint

ESLint is used for linting Javascript code.

Airbnb config is used as default.

To run a linter, you can use the following npm-scripts:

yarn lint:js —to run a linter

yarn lint:js:fix —to run a linter with autofix

CSS(styled-components)

To lint css code, stylelint is used. The linter checks your code for typos and spelling mistakes. To run the linter, you can use yarn lint:css script

Find out how best to use SASS extensions for custom CSS variables here.

Airbnb styleguide links

Javascript
React

To run both linters, you can use yarn lint:all script

JSDoc

The optimal solution to make your code more readable and cleaner is to use JSDoc. The project doesn’t use JSDoc by default, but you can easily add it using the following helpful links:

Running in production

To run the project in production, you can use yarn docker:prod script. This script does the following steps:

  • Download dependencies
  • Build the project
  • Run nginx to serve static content

Cypress

Cypress is a framework for end-to-end testing based on Javascript.

Why Cypress?

You can have 100% code coverage with unit tests, which test all your components separately, but your application can still fail when the components start to interact with each other. To prevent possible fails, you need to use e2e tests with Cypress. Cypress can test everything that works in a browser. To install cypress, you can use the instructions in the readme.

Typescript

To develop modern, big React applications, people most often use Typescript to prevent unexpected errors and problems. Typescript helps avoid primitive errors and makes Javascript clearer and more expressive. Typescript has its disadvantages, but the benefits most often outweigh them. If you want to use Typescript along with our boilerplate, you can use the instructions for adding it to a project in readme.

Gitlab CI

Gitlab CI is one of the easiest and most convenient tools to check and deploy your code anywhere. In our boilerplate, we use several steps to make it easy to deliver your code to production:

  • install — installing dependencies using yarn
  • lint — code linting yarn lint:all
  • test — running unit tests using yarn test:ci script; building and displaying coverage
  • pages — building the project yarn build
  • pages:deploy — deploy to gitlab-pages

Additional tricks

Here, I’ve collected a few tricks to make the development of your app easier and faster.

CLI

To create the component, you can use the following CLI-script:

yarn create:component MyComponent
Enter fullscreen mode Exit fullscreen mode

When you use this script, a folder with your component’s name will appear in your src/components folder. In this case, it will be the src/components/MyComponent folder.

VSCode-snippets

Here is a list of available snippets to quickly create some entities:

  • mdocmp — component
  • mdstyle — styled-components file
  • mdcompunit — unit tests for component
  • mdpage— page
  • mdhook — custom hook These snippets are automatically available in your VSCode because they are set up for the project. You can see and edit any snippet in the .vscode/madboiler-snippets.code-snippets file

Useful VSCode extensions

Here’s a list of the most useful VSCode extensions that make developing your React application easier and faster:

  • vscode-styled-components — styled-components support
  • Visual Studio IntelliCode — intelliSense for VSCode (AI-assit)
  • TODO Highlight — highlight your #todos
  • React PropTypes Intellisense — intelliSense for PropTypes
  • Prettier — for autoformatting
  • Path Intellisense — intelliSense for ES6 imports/exports
  • ESLint — lint highlight

Сonclusion

This article shows you the boilerplate that we actively use when creating new projects. The boilerplate includes everything you need and describes some additional useful things (such as typescript and cypress). Feel free to use our boilerplate, and if you have any questions or problems with it, we promise to help you. Thanks for reading!

Previously published at maddevs.io

Top comments (0)