When starting a new React project, there are a bevy of options available. Each has its own merits and shortcomings that leave room for comparison.
With tools like create-react-app, Next, or Gatsby, you’re entering a territory of abstract, advanced, and highly opinionated toolchain, which undoubtedly comes with several benefits. However, there are times when You might not need a toolchain, and that even leaves you with a lot to desire.
One thing you need to understand is that you can use as little or as much React as you want on your website. What this means is that React can be applied to specific portions of your website where you feel the need for it. That has been covered in Adding React to a Website.
But what if you could set up a new React app toolchain for a weekend/pet project, with the absolutely necessary packages to begin with, and include more sophisticated packages as you go along?
You could build a toolchain that includes things like bundling, linting, formatting etc., without the overhead of abstract and opinionated tools or the learning curve. You could also learn so much about how things work behind the scenes and how to put little bits of the pieces together to create something substantial.
The Replay is a weekly newsletter for dev and engineering leaders.
Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.
We’ll be building a toolchain that includes:
Keep in mind that this is not an in-depth look into each of these tools. Instead, we’re just going to go over the necessary bits and pieces to get you up and running. That being said, let’s get to it.
react-app-toolchain repoTo get started, you need to clone the repo here.
The folder contains the needed folders and files to get started with.
The starter folder looks like this:
- public
- index.html
- src
- components
- Users.js
- App.js
- app.css
- app.scss
- .eslintrc
- .prettierignore
- .prettierrc
- .gitignore
- package.json
Now that we have created the necessary folders, we can include the two necessary dependencies for React:
reactreact-domyarn add react react-dom
This will update the package.json to include the added dependencies. The versions of the package might be different.
"dependencies": {
"react": "^16.13.1",
"react-dom": "^16.13.1"
}
While you can technically write some React here, it’d be of no use unless we add a bundler and render the app to the DOM.
You can check the
include-reactbranch for the latest code.
The bundler we’d be using is Parcel. Parcel is a zero-config, lightweight, and fast web application bundler:
yarn add parcel-bundler -D
At this point your package.json file (except for the dependencies version numbers) should look like:
{
"name": "react-app-toolchain",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"react": "^16.13.1",
"react-dom": "^16.13.1"
},
"devDependencies": {
"parcel-bundler": "^1.12.4"
}
}
You can now write some components and run the app with Parcel.
Update the public/index.html file to:
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>React App Toolchain</title>
</head>
<body>
<!-- We'd render the react app in here -->
<div id="app"></div>
<!-- We reference the entry point of our React app here -->
<script src="../src/App.js"></script>
</body>
</html>
And src/App.js to:
// src/App.js
import React from "react";
import ReactDOM from "react-dom";
function App() {
return <h1>Hello World!</h1>;
}
ReactDOM.render(<App />, document.getElementById("app"));
Then add a start script to package.json that instructs parcel on the entry point of your app:
{
"name": "react-app-toolchain",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "parcel public/index.html"
},
"dependencies": {
"react": "^16.13.1",
"react-dom": "^16.13.1"
},
"devDependencies": {
"parcel-bundler": "^1.12.4"
}
}
Now you can run the command yarn start and visit the running app at localhost:1234, or the port that Parcel runs the app on.

You can check the
include-bundlerbranch for the latest code.
To format our files, we’ll use Prettier. Prettier is an opinionated code formatter that works with technologies like HTML, CSS, and JavaScript.
yarn add prettier -D
Then add two config files:
.prettierrc will contain the desired prettier rules, and.prettierignore will contain the files/directories we do not need prettier to format, such as the node_modulesAdd the following rules to the .prettierrc file:
{
"arrowParens": "avoid",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"printWidth": 120,
"proseWrap": "always",
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all",
"useTabs": false,
"jsxSingleQuote": true
}
And to prettierignore:
node_modules build coverage public dist .cache
You also need a command for formatting. Add another scripts to package.json — this time, format. This updates the package.json to:
{
"name": "react-app-toolchain",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "parcel public/index.html",
"format": "prettier \"src/**/*.js\" --write"
},
"dependencies": {
"react": "^16.13.1",
"react-dom": "^16.13.1"
},
"devDependencies": {
"parcel-bundler": "^1.12.4",
"prettier": "^2.0.5"
}
}
Running the format script instructs Prettier to format all files ending with js in the src directory.
If you run the command yarn format, the double quotes in App.js should now be single quotes. Semi-colons should also be included where omitted, and the other rules in .prettierrc should be applied.
You’d have to keep running
yarn formatto format your files. If you’re using VSCode, you can install the Prettier extension, then turn on theeditor.formatOnSavesettings. When you save, Prettier will automatically format your file — even without theformatscript.You can check the
include-formattingbranch for the latest code.
You’ve added those Prettier rules, but they’re not enforced. ESLint is a JavaScript linter that helps you find problems in your code.
yarn add eslint babel-eslint eslint-plugin-react-hooks eslint-config-prettier eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react -D
With the previous, command, you’ve not only added eslint, but also added packages like:
eslint-plugin-react-hookseslint-plugin-importbabel-eslint allows linting for all valid Babel codeeslint-config-prettier settles the conflict between ESLint and Prettiereslint-plugin-jsx-a11y is a static AST checker for accessibility rules on JSX elements that helps with issues like not including a alt-text in imageseslint-plugin-react helps with react specific ESLint rulesThen update the .eslintrc file with the configs:
{
"extends": [
"eslint:recommended",
"plugin:import/errors",
"plugin:react/recommended",
"plugin:jsx-a11y/recommended",
"plugin:react-hooks/recommended",
"prettier",
"prettier/react"
],
"plugins": ["react", "import", "jsx-a11y"],
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"env": {
"es6": true,
"browser": true,
"node": true
},
"rules": {
"semi": ["warn", "always"],
"quotes": ["warn", "single"]
},
"settings": {
"react": {
"version": "detect"
}
}
}
Here, rules instructs ESLint to give a warning when a semi-colon is omitted or when the single quote is not used. ecmaFeatures notifies ESLint that JSX has been used, and env contains the environments where the app will be run.
Now you need a lint script to help lint your code. The package.json scripts gets updated to:
"scripts": {
"start": "parcel public/index.html",
"format": "prettier \"src/**/*.js\" --write",
"lint": "eslint \"src/**/*.{js,jsx}\""
},
Now you can run yarn lint to format all files in src that ends with .js or .jsx extension.
You’d have to keep running
yarn lintto lint your files. If you’re using VSCode, you can install the ESlint extension. This will make sure you get error messages right in your file as you make them, and as ESlint detects them.
You can check the
include-lintingbranch for the latest code.
There are many options to go with when it comes to styling. Let’s go over:
Adding a CSS file is straightforward. Update the app.css and app.scss files:
/* src/app.css */
.clr-red {
color: red;
}
// src/app.scss
.bg-clr-black {
background-color: black;
}
Also, add the necessary classes to the h1 in App.js and import both app.css and app.scss:
// src/App.js
import React from 'react';
import ReactDOM from 'react-dom';
import './app.css';
import './app.scss';
function App() {
return <h1 className='clr-red bg-clr-black'>Hello World!</h1>;
}
ReactDOM.render(<App />, document.getElementById('app'));
Because you used sass, Parcel will automatically install and add
sassto a list of the dependencies inpackage.json.
If you want to use a CSS-in-JS solution, like styled components, you’d have to install it.
yarn add styled-components -D
// src/App.js
import React from 'react';
import ReactDOM from 'react-dom';
import './app.css';
import './app.scss';
import styled from 'styled-components';
const StyledApp = styled.div`
text-transform: uppercase;
`;
function App() {
return (
<StyledApp>
<h1 className='clr-red bg-clr-black'>Hello World!</h1>
</StyledApp>
);
}
ReactDOM.render(<App />, document.getElementById('app'));
You can check the
include-stylingbranch for the latest code.
Fetching data is straightforward. Let’s fetch some data here in App.js.
// src/App.js
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import './app.css';
import './app.scss';
import styled from 'styled-components';
import Users from './components/Users';
const StyledApp = styled.div`
text-transform: uppercase;
`;
function App() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('https://randomuser.me/api/?format=json&results=10')
.then(res => res.json())
.then(res => {
const results = res.results.map(({ picture, name }) => {
return {
thumbnail: picture.thumbnail,
name: `${name.title} ${name.first} ${name.last}`,
};
});
setUsers([...results]);
});
}, []);
return (
<StyledApp>
<h1 className='clr-red bg-clr-black'>Hello World!</h1>
<Users {...{ users }} />
</StyledApp>
);
}
ReactDOM.render(<App />, document.getElementById('app'));
You also need to update Users.js:
// src/components/Users.js
import React from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
const StyledUsers = styled.div`
.card {
display: flex;
border: 1px solid salmon;
padding: 20px;
}
.photo {
margin-right: 20px;
display: flex;
align-items: flex-start;
}
.img {
border-radius: 50%;
}
`;
function Users({ users }) {
return (
<StyledUsers>
{users.map((user, index) => (
<div className='card' key={index}>
<div className='photo'>
<img className='img' src={user.thumbnail} alt={user.name} />
</div>
<h1 className='name'>{user.name}</h1>
</div>
))}
</StyledUsers>
);
}
Users.propTypes = {
users: PropTypes.array,
};
export default Users;
If you wanted, we could also use Axios or Next’s SWR.
ESLint may alert you about prop-types. First, install it with the command
yarn add prop-types -Dand updateUsers.jsaccordingly.
The users list should now be displayed:

You can check the
include-data-fetchingbranch for the latest code.
The final code is in the final branch.
That’s a lot of build tools if you don’t know where to look, and even if you do, bringing them together to run your app can be tedious. Most especially when you have to keep up with various updates and backwards dependencies. And at this point, my guess is that you’re probably thinking: why bother? Why can’t you just use one of the already-made tool-chain, so you can focus on what really matters – building your app?
And to be fair, there’s a reasonable justification there.
But there’s power in knowing the inner workings of how these tools works, because then you can determine what resource is important to your development and vice-versa. Also, you could create a boilerplate just like React Boilerplate so as to avoid doing this over. You might also want to check out Create App wizard to learn more or a different way on how these toolchains can be wired up.
There are many more tools you can add to your toolchain, like routing (with React Router) and form management (with Formik), but none of these are needed to make React work so we didn’t touch on them.
Your toolchain is yours to use and mutate as you deem fit. And there’s nothing stopping you from having one. So, why not create one?
Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not
server-side
$ npm i --save logrocket
// Code:
import LogRocket from 'logrocket';
LogRocket.init('app/id');
// Add to your HTML:
<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>

CSS text-wrap: balance vs. text-wrap: prettyCompare and contrast two CSS components, text-wrap: balance and text-wrap: pretty, and discuss their benefits for better UX.

Remix 3 ditches React for a Preact fork and a “Web-First” model. Here’s what it means for React developers — and why it’s controversial.

A quick guide to agentic AI. Compare Autogen and Crew AI to build autonomous, tool-using multi-agent systems.

Compare the top AI development tools and models of November 2025. View updated rankings, feature breakdowns, and find the best fit for you.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up now