Guide to Starting a React App: Tips on Components, Tools & Best Practices

Written by

Most developers know that React is one of the most popular and useful JavaScript libraries when it comes to building web user interfaces, as it helps to create complex web applications in a modular, and efficient way. But usually, React developers don't just use React to work.

Most times, it is necessary to use additional tools, libraries, or frameworks and that's when things get tricky, how do you know which one to learn and use when there are so many of them available? In this article we've got you covered, we will explore the key points to consider when creating a React app. Additionally, we will share some tips and best practices related to structuring your React project, improving your React skills, SEO, accessibility, and performance in 2023. Let's get started!

Class Components vs. Functional Components

The distinction between class components and functional components is one of the first things you should understand when starting a React project in 2023. Class components are the standard approach for creating React components that use ES6 classes and lifecycle functions. They are simpler to read and comprehend for developers who are familiar with object-oriented programming (OOP) ideas. Functional components are the newest approach to developing React components that use simple JavaScript functions. They do not have states or lifecycles on their own. To solve that, it is necessary to use hooks, and they are generally simpler and easier to read than class components.

Hooks were introduced in React 16.8 to allow the usage of state, effects, context, and custom logic in functional components without the need to write classes. Hooks make your code more concise, understandable, and reusable. Additionally, they enable sophisticated features such as concurrent mode and suspense. Some of the most popular hooks are useState to handle the local state within the component; useEffect, which is used to synchronize a component with an external system by performing side effects in a declarative way, such as fetching data or modifying the DOM; and useSelector to access and subscribe to the Redux store state.

Here is an example of using useState with a simple form that collects the user’s name and email:

function Form() {
  // State variables for name and email
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");

  // Handle the form submission
  function handleSubmit(event) {
	event.preventDefault();
	alert(`Name: ${name}, Email: ${email}`);
  }

  // Handle the name input change
  function handleNameChange(event) {
	setName(event.target.value); // update the name state with the input value
  }

  // Handle the email input change
  function handleEmailChange(event) {
	setEmail(event.target.value); // update the email state with the input value
  }

  // Return the JSX code to render the form component
  return (
	<div className="Form">
  	<h1>Simple Form</h1>
  	<form onSubmit={handleSubmit}>
    	<label htmlFor="name">Name:</label>
    	<input
      	type="text"
      	id="name"
      	value={name}
      	onChange={handleNameChange}
      	required
    	/>
    	<label htmlFor="email">Email:</label>
    	<input
      	type="email"
      	id="email"
      	value={email}
      	onChange={handleEmailChange}
      	required
    	/>
    	<button type="submit">Submit</button>
  	</form>
	</div>
  );
}

And here's an example of the custom hook useFetch, which retrieves data from an API and stores it in state variables by utilizing the useState and useEffect hooks:

// Declaring the custom hook
function useFetch(url) {
  // Defining state variables
  const [data, setData] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  // Effect to fetch data from the URL and update variables
  useEffect(() => {
	const fetchData = async () => {
  	try {
    	const response = await fetch(url);
    	const json = await response.json();
    	setData(json);
  	} catch (error) {
    	setError(error);
  	} finally {
    	setIsLoading(false);
  	}
	};

	fetchData();
  }, [url]);

  return { data, isLoading, error };
}

export default useFetch;

As you can see, hooks make it simple to add state, effects, context, and custom logic to functional components. They help in avoiding some common issues with class components, such as 'this' binding issues, unnecessary re-rendering, and complicated lifecycle methods.

As a result, we propose adopting functional components and hooks as the default manner for designing components in React. However, if you need to use class components for some reason (for example, compatibility with legacy code or third-party libraries), you definitely can. You only need to make sure you grasp the distinctions and trade-offs between class and functional components.

Tools and tips for starting a React project

Another factor to consider when beginning a React project is the many tools and frameworks available for developing and deploying your React application. While you can always use plain React and configure your own build tools and server, this is time-consuming and tedious. As a result, several tools and frameworks provide a ready-made environment for developing and delivering React apps with minimal configuration and difficulty.

Create-react-app (CRA) was and probably still is one of the favorite tools for rapidly and easily starting a React project until very recently. Today, it is no longer recommended by the React team. Keeping this in mind, here we give some options that may be more beneficial to learn and use.

  • Next.js: A powerful tool that assists developers in creating React web applications that are quick and easy to locate in search engines. It includes several essential capabilities such as server-side rendering, static site generation, file-system routing, API routes, and code splitting, all of which make developing web applications much easier. Even beginners may use Next.js to create high-quality web applications that run rapidly and are SEO-friendly.
  • Vite: a build tool that uses esbuild to bundle your code faster and more efficiently than Webpack. Vite also includes a development server with hot module replacement, code splitting, TypeScript support, and other features. Vite is compatible with any framework, although it is especially well-suited for React.

Other options include Remix, React Starter Kit, and Razzle. Every tool has its own capabilities, advantages, and disadvantages. A one-size-fits-all solution for every project does not exist; for this reason, when deciding on the right tool or framework for a new project, keep in mind its requirements, objectives, and preferences.

Finally, to make an informed decision, you should consider factors such as experience with web development, performance optimization, the need for SEO support, how the selected tool handles routing and data fetching, and how easy it is to deploy the app.

Using the new features of React 18

React 18 is the most recent major version of React published last year. It brings various new features and improvements to React making it more powerful and easier to use. Here are some of the most important ones:

  • Concurrent mode: This is a new rendering style that allows React to render many components simultaneously without interrupting the main thread. This increases your app's responsiveness and perceived performance, particularly on slow devices or networks. To enable concurrent mode, wrap your app in a <React.StrictMode> component and mount it with ReactDOM.createRoot rather than ReactDOM.render.
  • Suspense: This feature allows you to postpone rendering some components until their data or code is available. This simplifies the code in your app for dealing with loading states and fallbacks. Using suspense wraps your components in a <React.Suspense> component and dynamically loads your components or modules using React.lazy or import().
  • JSX transform: This new feature lets you write JSX without having to import React in every file, making your code more readable and consistent. You must change your bundler and Babel setups to support the new syntax to use JSX transform. For fragments, you may also utilize the new <> and </> syntax instead of <React.Fragment>.

Here’s an example using the three new features with a component that fetches some quotes from an API:

import ReactDOM from "react-dom";

// A component that fetches a random quote from an API
function Quote() {
  let [quote, setQuote] = useState(null);

  React.useEffect(() => {
	fetch("https://api.quotable.io/random")
  	.then((response) => response.json())
  	.then((data) => setQuote(data));
  }, []);

  // Return a loading message if the quote is not ready
  if (!quote) {
	return <p>Loading...</p>;
  }

  // Return the quote and the author
  return (
	<div>
  	<p>{quote.content}</p>
  	<p>— {quote.author}</p>
	</div>
  );
}

// A component that renders a button to refresh the quote
function RefreshButton() {
  let [refresh, setRefresh] = useState(false);

  // Use startTransition to mark the refresh as a transition
  function handleClick() {
	React.startTransition(() => {
  	setRefresh((prev) => !prev);
	});
  }

  // Return a button that triggers the refresh
  return <button onClick={handleClick}>Refresh</button>;
}

// The main app component that uses concurrent mode
function App() {
  return (
	<React.StrictMode>
  	<Suspense fallback={<p>Loading...</p>}>
    	<Quote />
    	<RefreshButton />
  	</Suspense>
	</React.StrictMode>
  );
}

// Render the app with concurrent mode
ReactDOM.createRoot(document.getElementById("root")).render(<App />);

Tips and best practices for structuring your React project

Now that you know about the tools and frameworks to start your new project, you need to think about how to structure it. The way you organize your files and folders, name your components, variables, and functions, manage states, format, and lint your code, and whether or not you implement TypeScript can have a big impact on the quality, readability, and maintainability of your code. 

While there is no definitive solution to how to structure your React project due to individual projects' demands and preferences, there are some general suggestions and best practices you can follow to make your project more consistent and organized. Here are a few examples:

  • For your files and folders, use a consistent naming convention. For example, you can use PascalCase for component files (i.e., FormComponent), camelCase for function files as well as for function and variable names (i.e., findMax), kebab-case for style files (i.e., form-container) and so on. You can also identify the type of file with a dot, such as .jsx for React components, .ts for TypeScript files, .test.js for test files, and so on.
  • Combine related files and folders. Components, for example, can be grouped by features, pages, or routes. You can also group styles, graphics, files, utilities, hooks and so on based on their usage or functionality. You can keep all of your source files in a src folder and all of the static files in a public folder.
  • For your components, take a modular and reusable approach. You can, for example, apply atomic design principles to divide your components into atoms, molecules, organisms, templates, and pages. Another good approach is to follow the single responsibility principle. To communicate state and logic among components, you may also utilize compound components or custom hooks.
  • Use TypeScript to detect problems and bugs early in the development process, increase code readability and documentation, and improve editor support and autocomplete functionality.
  • Make use of code formatters and linters like ESLint and Prettier. Linting and formatting code can assist you in enforcing consistent coding styles and standards, avoiding frequent errors and bad practices, and improving code quality and readability. ESLint is a code analysis tool that finds probable problems and flaws in your code. Prettier is a code formatting application that prepares your code based on established criteria and preferences.

These are some of the best practices and tips for structuring your React project. Of course, it is always possible to modify them to meet your specific requirements and preferences. The most important thing is to be consistent and adhere to the norms and conventions of the tool or framework being used.

Optimizing your app for performance, SEO, and accessibility

Another thing to consider when starting a React project is how to optimize the app's performance, accessibility, and SEO.

  • Performance: To increase the performance of your app, utilize tools like Lighthouse, WebPageTest, or Chrome DevTools to measure and evaluate it. To lower the size and loading time of your app you should also employ code splitting, lazy loading, caching, compression, and other approaches. To boost the responsiveness and perceived performance of your software you should also use concurrent mode and suspense. You should also minimize excessive re-rendering and instead use memoization and pure components to improve the performance of your app.
  • Accessibility: It's a good idea to follow the Web Content Accessibility Guidelines (WCAG) and employ semantic HTML elements, suitable labels, roles, and attributes to make your app accessible to all users. To make your software keyboard and screen-reader-friendly, leverage keyboard navigation, attention management, and ARIA landmarks. To make your app visually accessible, use contrast ratios, color schemes, font sizes, and other design features. 
  • SEO: Use server-side rendering or static site generation to render your app on the server and provide pre-rendered HTML to the browser to make your app SEO-friendly. This increases your app's crawlability and indexability by search engines. You should also employ meta tags, structured data, canonical URLs, sitemaps, robots.txt, and other SEO approaches to optimize your app for search engines. You should also use tools like Google Search Console or Bing Webmaster Tools to monitor and optimize your app's SEO performance.

Testing and deploying your app with confidence and ease

The last thing you need to know when starting a React project is how to confidently and easily test and deploy your app. These are some of the tools and services available to you:

  • Testing: Unit testing, integration testing, end-to-end testing, snapshot testing, visual testing, and accessibility testing should all be used to test your software. To test the functionality, behavior, appearance, and accessibility of your app, you can use tools such as Jest, React Testing Library, Enzyme, Cypress, Puppeteer, Storybook, or Chromatic. You should also measure and report your code coverage using tools like Codecov or Coveralls.
  • Deploying: While it's not strictly necessary you should use a continuous integration and continuous delivery (CI/CD) pipeline to launch your app, to automate the building, testing, and delivery of your app. You can use technologies like GitHub Actions, CircleCI, Travis CI, or Jenkins to construct and execute your CI/CD pipeline. You can also host and serve your app on the cloud using services such as Netlify, Vercel, Firebase Hosting, or AWS Amplify. You should also use tools like Sentry or LogRocket to monitor and debug the performance and faults of your app.

As you can see, React is an excellent tool for developing modern web applications. It contains many new features and improvements that make it more powerful and user-friendly. It also provides a robust ecosystem of tools and libraries to help you set up your development environment, optimize your app for performance, improve accessibility, increase SEO, and confidently test and deploy your app.

Frequently Asked Questions