Sunsetting Create React App
When we released Create React App in 2016, there was no clear way to build a new React app. To create a React app, you had to install a bunch of tools and wire them up together yourself to support basic features like JSX, linting, and hot reloading. This was very tricky to do correctly, so the community created boilerplates for common setups. However, boilerplates were difficult to update and fragmentation made it difficult for React to release new features. Create React App solved these problems by combining several tools into a single recommended configuration. This allowed apps a simple way to upgrade to new tooling features, and allowed the React team to deploy non-trivial tooling changes (Fast Refresh support, React Hooks lint rules) to the broadest possible audience. This model became so popular that there’s an entire category of tools working this way today.
Deprecating Create React App
Although Create React App makes it easy to get started, there are several limitations that make it difficult to build high performant production apps. In principle, we could solve these problems by essentially evolving it into a framework. However, since Create React App currently has no active maintainers, and there are many existing frameworks that solve these problems already, we’ve decided to deprecate Create React App. Starting today, if you install a new app, you will see a deprecation warning:How to Migrate to a Framework
We recommend creating new React apps with a framework. All the frameworks we recommend support client-side rendering (CSR) and single-page apps (SPA), and can be deployed to a CDN or static hosting service without a server. For existing apps, these guides will help you migrate to a client-only SPA:- Next.js’ Create React App migration guide
- React Router’s framework adoption guide.
- Expo webpack to Expo Router migration guide
How to Migrate to a Build Tool
If your app has unusual constraints, or you prefer to solve these problems by building your own framework, or you just want to learn how react works from scratch, you can roll your own custom setup with React using Vite, Parcel or Rsbuild. For existing apps, these guides will help you migrate to a build tool:- Vite Create React App migration guide
- Parcel Create React App migration guide
- Rsbuild Create React App migration guide
Limitations of Build Tools
Create React App and build tools like it make it easy to get started building a React app. After runningnpx create-react-app my-app, you get a fully configured React app with a development server, linting, and a production build.
For example, if you’re building an internal admin tool, you can start with a landing page:
export default function App() {return (<div><h1>Welcome to the Admin Tool!</h1></div>)}Routing
Create React App does not include a specific routing solution. If you’re just getting started, one option is to useuseState to switch between routes. But doing this means that you can’t share links to your app - every link would go to the same page - and structuring your app becomes difficult over time:
import {useState} from 'react';import Home from './Home';import Dashboard from './Dashboard';export default function App() {// ❌ Routing in state does not create URLsconst [route, setRoute] = useState('home');return (<div>{route === 'home' && <Home />}{route === 'dashboard' && <Dashboard />}</div>)}import {RouterProvider, createBrowserRouter} from 'react-router';import Home from './Home';import Dashboard from './Dashboard';// ✅ Each route has it's own URLconst router = createBrowserRouter([{path: '/', element: <Home />},{path: '/dashboard', element: <Dashboard />}]);export default function App() {return (<RouterProvider value={router} />)}/dashboard and the app will navigate to the dashboard page . Once you have a routing library, you can add additional features like nested routes, route guards, and route transitions, which are difficult to implement without a routing library.
There’s a tradeoff being made here: the routing library adds complexity to the app, but it also adds features that are difficult to implement without it.
Data Fetching
Another common problem in Create React App is data fetching. Create React App does not include a specific data fetching solution. If you’re just getting started, a common option is to usefetch in an effect to load data.
But doing this means that the data is fetched after the component renders, which can cause network waterfalls. Network waterfalls are caused by fetching data when your app renders instead of in parallel while the code is downloading:
export default function Dashboard() {const [data, setData] = useState(null);// ❌ Fetching data in a component causes network waterfallsuseEffect(() => {fetch('/api/data').then(response => response.json()).then(data => setData(data));}, []);return (<div>{data.map(item => <div key={item.id}>{item.name}</div>)}</div>)}export async function loader() {const response = await fetch(`/api/data`);const data = await response.json();return data;}// ✅ Fetching data in parallel while the code is downloadingexport default function Dashboard({loaderData}) {return (<div>{loaderData.map(item => <div key={item.id}>{item.name}</div>)}</div>)}Code Splitting
Another common problem in Create React App is code splitting. Create React App does not include a specific code splitting solution. If you’re just getting started, you might not consider code splitting at all. This means your app is shipped as a single bundle:- bundle.js 75kb- core.js 25kb- home.js 25kb- dashboard.js 25kbReact.lazy. However, this means that the code is not fetched until the component renders, which can cause network waterfalls. A more optimal solution is to use a router feature that fetches the code in parallel while the code is downloading. For example, React Router provides a lazy option to specify that a route should be code split and optimize when it is loaded:
import Home from './Home';import Dashboard from './Dashboard';// ✅ Routes are downloaded before renderingconst router = createBrowserRouter([{path: '/', lazy: () => import('./Home')},{path: '/dashboard', lazy: () => import('Dashboard')}]);And more…
These are just a few examples of the limitations of Create React App. Once you’ve integrated routing, data-fetching, and code splitting, you now also need to consider pending states, navigation interruptions, error messages to the user, and revalidation of the data. There are entire categories of problems that users need to solve like:- Accessibility
- Asset loading
- Authentication
- Caching
- Error handling
- Mutating data
- Navigations
- Optimistic updates
- Progressive enhancement
- Server-side rendering
- Static site generation
- Streaming