Missed JavaScript Days 2026? The Replays Are Now Available – Watch Now!

How to Create a React App with TypeScript in 2026: Step-by-Step Guide

Oct 6, 2022 ✎Oct 27, 2026 36932 Views

Get a summary of this article:

If you searched for “create react app typescript”, you probably want one thing: a fresh React project with TypeScript already set up. In 2026 that’s a single command and well under a minute:


    npm create vite@latest my-app -- --template react-ts

TL;DR

  • Fastest setup: use Vite with the react-ts template. It’s the standard way to start a React single-page app from scratch.
  • Four steps: install Node.js 22 or later, create the project, install dependencies, start the dev server.
  • Need SEO or server rendering? Use Next.js instead; TypeScript is on by default. (The React docs recommend starting with a framework like this for most new apps.)
  • Still on Create React App? Your app keeps running, and moving it to Vite usually takes about a day.
  • Why TypeScript? It catches mistakes before your users do and keeps big React apps maintainable.
  • Building data-heavy screens? Add ReExt for ready-made grids, forms and charts.

Below, we walk through the setup step by step, explain what each config file does, show the TypeScript patterns you’ll use every day, and cover migrating an older CRA project.

How to Create a React App with TypeScript in 2026: Step-by-Step Guide

What happened to Create React App?

Create React App (CRA) helped millions of developers get started with React. But it was built on webpack 5 and couldn’t keep up with newer tools. The new react.dev docs stopped recommending it when they launched in March 2023, and the React team formally deprecated it in February 2025.

The good news is that today’s setup is just as simple, and a lot faster. Vite’s dev server starts in about a second and updates the page almost instantly when you save.

Create React App alternatives in 2026

Pick based on what you’re building. Vite is a build tool for single-page apps; Next.js, React Router and Expo are full React frameworks.

Tool Best for Command TypeScript
Vite SPAs, dashboards, internal tools npm create vite@latest my-app — –template react-ts Built-in template
Next.js SEO sites, SaaS, full-stack apps npx create-next-app@latest my-app On by default
React Router v7 Full-stack React with web-standard data loading (formerly Remix) npx create-react-router@latest my-app On by default
Expo React Native mobile plus web npx create-expo-app@latest my-app On by default

Create React App vs Vite vs Next.js

Create React App Vite (react-ts) Next.js
Status in 2026 Deprecated (existing apps still run) Actively developed Actively developed
TypeScript setup Template option Built-in template On by default
Dev server start Tens of seconds on large apps About a second A few seconds
Updates on save Slower refresh Near-instant Fast
Server rendering / SEO No No (client-side) Yes
Best for Maintaining older apps New SPAs and dashboards SEO sites, SaaS, full-stack apps

Already using the create-react-app command? Plenty of older tutorials still use npx create-react-app my-app –template typescript. Apps built that way keep working, but for anything new, the Vite command gives you the same React + TypeScript setup with much faster builds and maintained tooling.

Step by step: create a React app with Vite and TypeScript

Step 1: Check Node.js

Install Node.js 22 or 24 LTS. Current Vite versions need at least Node.js 20.19 or 22.12, so older installs will throw an error.


    node --version   # should print v22.x or v24.x

Step 2: Create the project

Run the command for your package manager. Each one creates the same React + TypeScript project.


    # npm
    npm create vite@latest my-react-app -- --template react-ts

    # pnpm
    pnpm create vite my-react-app --template react-ts

    # yarn
    yarn create vite my-react-app --template react-ts

    # bun
    bun create vite my-react-app --template react-ts

Prefer clicking through prompts? Run npm create vite@latest with no flags, then choose React and TypeScript.

Step 3: Install dependencies


    cd my-react-app
    npm install

That’s it for types: the template already includes typescript, @types/react and @types/react-dom.

Step 4: Start the dev server


    npm run dev

Open the local URL it prints (usually http://localhost:5173). Edit src/App.tsx, save, and the page updates instantly.

Step 5: Write your first typed component

Here’s a small counter with optional props and a callback. Notice there’s no React.FC: typing the props parameter directly is the simpler, more common style today.


    // src/components/Counter.tsx
    import { useState } from 'react';

    type CounterProps = {
      initialValue?: number;
      step?: number;
      onValueChange?: (value: number) => void;
    };

    export function Counter({ initialValue = 0, step = 1, onValueChange }: CounterProps) {
      const [count, setCount] = useState(initialValue);

      const update = (next: number) => {
        setCount(next);
        onValueChange?.(next);
      };

      return (
        <div className="counter">
          <button onClick={() => update(count - step)}>-</button>
          <span className="counter-value">{count}</span>
          <button onClick={() => update(count + step)}>+</button>
        </div>
      );
    }

Use it in App.tsx with <Counter step={5} onValueChange={(v) => console.log(v)} />. Pass step=”5″ as a string and TypeScript flags it before you even run the app.

Suggested folder structure

The template starts small. As the app grows, a structure like this keeps things findable:


    src/
      components/   reusable UI pieces
      features/     screens grouped by feature (users/, billing/)
      hooks/        custom hooks
      lib/          API clients and utilities
      types/        shared TypeScript types
      App.tsx
      main.tsx

What the Vite React TypeScript template includes

A new project gives you:

  • index.html in the project root (not in /public, as in CRA)
  • vite.config.ts with the React plugin
  • tsconfig.json, which points to tsconfig.app.json (your app code) and tsconfig.node.json (the Vite config)
  • eslint.config.js, a flat ESLint config with typescript-eslint and React Hooks rules already wired up
  • src/main.tsx, which mounts the app, and src/App.tsx, your first component

The scripts you’ll use every day:

Command What it does
npm run dev Starts the dev server with instant hot reload
npm run build Type-checks with tsc -b, then builds for production
npm run preview Serves the production build locally
npm run lint Runs ESLint with TypeScript rules

Want automatic formatting too? Add Prettier and turn off ESLint rules that clash with it:


    npm install -D prettier eslint-config-prettier

The tsconfig files, explained

The root tsconfig.json just links the two project configs together:


    {
      "files": [],
      "references": [
        { "path": "./tsconfig.app.json" },
        { "path": "./tsconfig.node.json" }
      ]
    }

Your app settings live in tsconfig.app.json. The template’s version looks roughly like this; you rarely need to change it:


    {
      "compilerOptions": {
        "target": "ES2022",
        "lib": ["ES2022", "DOM", "DOM.Iterable"],
        "module": "ESNext",
        "moduleResolution": "bundler",
        "jsx": "react-jsx",
        "strict": true,
        "noEmit": true,
        "isolatedModules": true,
        "skipLibCheck": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "noFallthroughCasesInSwitch": true
      },
      "include": ["src"]
    }

The options that matter most:

Option Recommended value Why it matters
strict true Turns on the checks that catch most real bugs
jsx react-jsx Modern JSX transform, so you don’t import React in every file
moduleResolution bundler Matches how Vite resolves imports
noEmit true Vite builds the code; tsc only type-checks
isolatedModules true Required for fast per-file compilers
target ES2022 Modern output for current browsers

TypeScript patterns every React app needs

Get these six patterns right once, and the rest of the app mostly follows.

Props and children

Describe props with a type or interface, and type children as React.ReactNode, which covers elements, strings, numbers, arrays and null.


    type CardProps = {
      title: string;
      children: React.ReactNode;
    };

    export function Card({ title, children }: CardProps) {
      return <section><h2>{title}</h2>{children}</section>;
    }

State and refs

TypeScript infers simple state. Add a type when the starting value doesn’t tell the whole story, and give refs the element type they point to.


    const [user, setUser] = useState<User | null>(null);
    const inputRef = useRef<HTMLInputElement>(null);

    inputRef.current?.focus(); // safe: current may be null

Reducers

List every action in a union type, so the reducer can only ever receive valid actions.


    type Action =
      | { type: 'add'; item: Item }
      | { type: 'remove'; id: number }
      | { type: 'clear' };

    function cartReducer(state: Item[], action: Action): Item[] {
      switch (action.type) {
        case 'add': return [...state, action.item];
        case 'remove': return state.filter((i) => i.id !== action.id);
        case 'clear': return [];
      }
    }

Events

Use React’s event types so event.target is typed correctly.


    const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => setName(e.target.value);
    const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); save(); };

Context

Create context with a type that allows undefined, then wrap it in a hook that throws a clear error if a component uses it outside the provider.


    const ThemeContext = createContext<'light' | 'dark' | undefined>(undefined);

    export function useTheme() {
      const theme = useContext(ThemeContext);
      if (!theme) throw new Error('useTheme must be used inside ThemeProvider');
      return theme;
    }

Environment variables

Vite only exposes variables that start with VITE_. Read them with import.meta.env, and declare their types so typos are caught:


    // src/vite-env.d.ts (create it if your template doesn't include one)
    /// <reference types="vite/client" />

    interface ImportMetaEnv {
      readonly VITE_API_URL: string;
    }

    interface ImportMeta {
      readonly env: ImportMetaEnv;
    }

Then use import.meta.env.VITE_API_URL anywhere in your app. Remember these values ship to the browser, so never put secrets in them.

Add path aliases, tests and deployment

These three steps aren’t in the template, but almost every real project adds them in the first week.

Path aliases: @/components instead of ../../../components

Tell both Vite and TypeScript about the alias. First install Node’s types so vite.config.ts can use path:


    npm install -D @types/node


    // vite.config.ts
    import { defineConfig } from 'vite';
    import react from '@vitejs/plugin-react';
    import path from 'node:path';

    export default defineConfig({
      plugins: [react()],
      resolve: {
        alias: { '@': path.resolve(__dirname, './src') },
      },
    });

Then add the matching setting to tsconfig.app.json:


    "compilerOptions": {
      "baseUrl": ".",
      "paths": { "@/*": ["./src/*"] }
    }

Now import { Counter } from ‘@/components/Counter’ works everywhere.

Testing with Vitest and Testing Library

Vitest reuses your Vite config, so there’s almost nothing to set up, and its API is close to Jest’s.


    npm install -D vitest jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event

Add a test block to vite.config.ts (the reference line gives you types for it):


    /// <reference types="vitest/config" />
    // ...inside defineConfig({ ... })
    test: {
      environment: 'jsdom',
      setupFiles: './src/setupTests.ts',
    },


    // src/setupTests.ts
    import '@testing-library/jest-dom/vitest';
    import { afterEach } from 'vitest';
    import { cleanup } from '@testing-library/react';

    afterEach(() => cleanup()); // reset the DOM between tests

A first test for the counter:


    // src/components/Counter.test.tsx
    import { test, expect } from 'vitest';
    import { render, screen } from '@testing-library/react';
    import userEvent from '@testing-library/user-event';
    import { Counter } from './Counter';

    test('increments by step', async () => {
      render(<Counter step={2} />);
      await userEvent.click(screen.getByText('+'));
      expect(screen.getByText('2')).toBeInTheDocument();
    });

Add “test”: “vitest” to your package.json scripts and run npm test.

Deploying your app

npm run build writes a static site to the dist/ folder, so you can host it almost anywhere: Vercel, Netlify, Cloudflare Pages, GitHub Pages, or S3 with a CDN.

One thing catches people out: if you use client-side routing, configure the host to send every unknown path to index.html. Otherwise refreshing a page like /settings returns a 404.

Next steps: UI components, a data grid and dashboard layouts

Once the app runs, most projects need three things next: a component library, a data grid, and a layout for dashboards.

Choose a React component library

A component library gives you ready-made buttons, inputs, dialogs, menus and date pickers. For a TypeScript project, check three things before you pick one:

  • Full TypeScript types for every component and prop
  • Accessibility built in: keyboard support and ARIA
  • Whether it covers the advanced pieces you’ll need later, like grids and charts

MUI, Ant Design and Mantine are popular for general apps. For data-heavy enterprise apps, ReExt gives you 140+ React components, including grids, forms and charts, that work inside a Vite or Next.js project.

Add a React data grid

A plain HTML table is fine for a few dozen rows. Once users need sorting, filtering, editing, grouping or thousands of rows, you need a proper data grid. Look for virtual scrolling, typed column definitions and Excel export. ReExt’s grid handles 25,000+ rows with filtering, sorting and selection built in.

Build dashboard layouts with react-grid-layout

For dashboards where users drag and resize panels, react-grid-layout is the most widely used option. Install it, import its stylesheet, and give each panel a typed layout entry with an ID, position and size. Typing the layout catches mistakes such as a missing panel ID before the app runs. For enterprise dashboards that combine layouts, grids and charts, ReExt’s layout components cover all three in one package.

Enterprise React with ReExt and Ext JS 8.0

We built ReExt so React developers can use Ext JS 8.0’s 140+ enterprise UI components without learning a new framework. ReExt 1.2 works like any React component library inside your Vite or Next.js app. Here’s a typed employee grid:


    // React component using an Ext JS grid via ReExt
    import { ExtGrid } from '@sencha/reext';

    interface Employee {
      id: number;
      name: string;
      department: string;
      salary: number;
      hireDate: string;
    }

    type EmployeeGridProps = {
      employees: Employee[];
      onSelectionChange: (selected: Employee[]) => void;
    };

    export function EmployeeGrid({ employees, onSelectionChange }: EmployeeGridProps) {
      const columns = [
        { text: 'Name', dataIndex: 'name', width: 200 },
        { text: 'Department', dataIndex: 'department', width: 150 },
        {
          text: 'Salary',
          dataIndex: 'salary',
          width: 120,
          renderer: (value: number) => '$' + value.toLocaleString(),
        },
        { text: 'Hire Date', dataIndex: 'hireDate', width: 120, xtype: 'datecolumn', format: 'M d, Y' },
      ];

      return (
        <ExtGrid
          title="Employee Directory"
          store={{ fields: ['id', 'name', 'department', 'salary', 'hireDate'], data: employees }}
          columns={columns}
          height={400}
          selModel={{ type: 'checkboxmodel', mode: 'MULTI' }}
          onSelectionChange={onSelectionChange}
          plugins={['gridfilters']}
        />
      );
    }

What you get without extra libraries:

  • Grid performance: virtual scrolling and buffered rendering hold 60 fps at 100,000+ rows, where standard React tables struggle.
  • Forms: complex business forms with field dependencies, async validation and custom layouts.
  • Accessibility: ARIA support and keyboard navigation that meet WCAG 2.2.

Create a React app with Next.js and TypeScript

Choose Next.js when you need server-side rendering, static generation, SEO or full-stack features in one project. It’s the most widely used React framework. Next.js 16 is the current major, and new projects get TypeScript, the App Router and Turbopack by default.


    npx create-next-app@latest my-next-app

Accept the defaults (TypeScript, ESLint, App Router), then run npm run dev. Next.js 16 needs Node.js 20.9 or later.

What you gain over a plain Vite SPA:

  • Server Components, which send less JavaScript to the browser
  • Server-side endpoints and Server Actions for full-stack features
  • Automatic code splitting, plus image and font optimisation

For a wider comparison, see our guide to the best React frameworks.

Create a React app with React Router v7 (formerly Remix)

Remix has merged into React Router v7. In framework mode it gives you nested routing, data loading with loaders and actions, server rendering and progressive enhancement, with TypeScript set up by default.


    npx create-react-router@latest my-app

React Router also generates route-specific types for you, so a loader’s return value is automatically typed in the component that uses it. Older tutorials that use npx create-remix are out of date.

How to migrate from Create React App to Vite

Most CRA projects move to Vite in a day. Work on a branch, and go step by step:

  1. Install Vite and the React plugin: npm install -D vite @vitejs/plugin-react
  2. Add vite.config.ts that uses the React plugin. Set server: { port: 3000 } if you want to keep CRA’s port.
  3. Move public/index.html to the project root, remove the %PUBLIC_URL% placeholders, and add <script type=”module” src=”/src/index.tsx”></script> before </body>.
  4. Rename environment variables from REACT_APP_* to VITE_*, read them with import.meta.env, and replace process.env.NODE_ENV with import.meta.env.MODE.
  5. Update package.json scripts: “dev”: “vite”, “build”: “tsc -b && vite build”, “preview”: “vite preview”.
  6. Update TypeScript: add src/vite-env.d.ts with /// <reference types=”vite/client” /> and set moduleResolution to “bundler”.
  7. Fix SVG imports: CRA’s import { ReactComponent as Logo } from ‘./logo.svg’ needs the vite-plugin-svgr plugin in Vite.
  8. Move tests to Vitest: most Jest tests run with few changes (see the testing section above).
  9. Remove react-scripts, run npm run dev, and fix any remaining import paths. Vite is stricter about JSX inside .js files, so rename those to .jsx or .tsx.

How to add TypeScript to an existing React project

You don’t have to convert everything at once. Vite compiles .js and .tsx files side by side, so the app keeps working throughout.

  1. Install TypeScript: npm install -D typescript @types/react @types/react-dom
  2. Add a tsconfig.json (the template settings above are a good start), with “allowJs”: true while you migrate.
  3. Rename files from .js/.jsx to .ts/.tsx one at a time, starting with small leaf components.
  4. Type props and state as you go. Turn on “strict”: true once most files are converted, then fix what it finds.

TypeScript vs JavaScript for React

TypeScript is now the default for new React projects; the Vite, Next.js and React Router templates all start with it. There’s research behind the shift, too: a study of public JavaScript bugs (Gao, Bird and Barr, 2017) found static type checking could have caught about 15% of them before release.

  • Fewer bugs reach users. Wrong prop types, missing state fields and mismatched event handlers fail at compile time, not in production.
  • A better editor. Autocomplete, safe renames and jump-to-definition all get dramatically better.
  • Easier teamwork. Types act as living documentation, so new teammates learn a large codebase faster.
  • No runtime cost. Types are stripped at build time, so bundles and runtime speed are the same as plain JavaScript.

Here’s what that looks like in practice:


    type UserProfileProps = {
      user: { id: number; name: string; email: string; avatar?: string };
      onEdit: (userId: number) => void;
    };

    export function UserProfile({ user, onEdit }: UserProfileProps) {
      return (
        <div className="user-profile">
          {user.avatar && <img src={user.avatar} alt={user.name} className="user-avatar" />}
          <h2>{user.name}</h2>
          <p>{user.email}</p>
          <button onClick={() => onEdit(user.id)}>Edit profile</button>
        </div>
      );
    }

Forget the email field, or call onEdit() without an ID, and your editor underlines it immediately. For a fuller comparison, read our guide to TypeScript vs JavaScript.

Performance and Core Web Vitals

A React + TypeScript app can score well on Core Web Vitals with a few habits. Aim for Google’s “good” thresholds:

Metric Good What it measures
Largest Contentful Paint (LCP) Under 2.5 s How fast the main content appears
Interaction to Next Paint (INP) Under 200 ms How fast the page responds to input (INP replaced FID in March 2024)
Cumulative Layout Shift (CLS) Under 0.1 How much the layout jumps around

The biggest single win is usually lazy loading routes, so users only download the code for the page they’re on:


    import { lazy, Suspense } from 'react';

    const Reports = lazy(() => import('./features/reports/Reports'));

    export function ReportsRoute() {
      return (
        <Suspense fallback={<p>Loading…</p>}>
          <Reports />
        </Suspense>
      );
    }

To see what’s actually in your bundle, build and open a visual map of it:


    npx vite-bundle-visualizer

Common quick wins:

  • Replace Moment.js with Day.js or date-fns.
  • Import individual functions (lodash-es/debounce) instead of whole libraries.
  • Lazy-load heavy components such as charts, editors and maps.
  • Set explicit width and height on images to avoid layout shift.

Common React TypeScript errors and how to fix them

Error Usual cause Fix
Property ‘x’ does not exist on type ‘IntrinsicAttributes & Props’ Passing a prop the component’s type doesn’t declare Add the prop to the props type, or remove it
Object is possibly ‘null’ Reading a ref or state before it’s set Check for null first, or use optional chaining (ref.current?.focus())
Type ‘string | undefined’ is not assignable to type ‘string’ An optional value passed where a value is required Provide a default, or narrow it with an if check
Cannot find name ‘process’ Using CRA’s process.env in Vite Use import.meta.env.VITE_* instead
JSX element implicitly has type ‘any’ Missing React type definitions Use the react-ts template, or install @types/react and @types/react-dom
Cannot find module ‘@/components/…’ Path alias set in Vite but not tsconfig (or the reverse) Add the alias to both vite.config.ts and tsconfig.app.json
Cannot find module ‘./logo.svg’ Missing asset type declarations Add /// <reference types=”vite/client” /> to src/vite-env.d.ts

Frequently asked questions

How do I create a React app with TypeScript in 2026?

Run npm create vite@latest my-app — –template react-ts for a single-page app, or npx create-next-app@latest my-app for a full-stack app. Both set up TypeScript for you.

Is Create React App still recommended?

No. It was deprecated in February 2025. The React team now recommends a framework such as Next.js or React Router for most new apps, or Vite when you want to build from scratch. Existing CRA apps keep working.

Does create-react-app with the TypeScript template still work?

It still installs and existing apps run, but it’s no longer maintained. For new projects, Vite gives you the same TypeScript setup with much faster builds.

What replaced Create React App?

Vite for single-page apps; Next.js, React Router v7 or Expo when you want a framework.

Should I migrate an existing CRA project to Vite?

Yes, for any project still in active development. The migration usually takes a day or two and gets you much faster builds and maintained tooling.

What’s the difference between Vite and Next.js?

Vite builds single-page apps that render in the browser. Next.js is a full framework with server rendering, static generation, server-side endpoints and built-in SEO features.

Should I use React.FC in TypeScript?

You can, but most teams now type the props parameter directly (function Card({ title }: CardProps)). It’s simpler, and it works better with default values and generics.

Type or interface for React props?

Either works. Interfaces can be extended and merged; types handle unions more naturally. Pick one convention and use it consistently across the codebase.

How do I use environment variables in a Vite React TypeScript app?

Prefix them with VITE_ in your .env file, read them with import.meta.env.VITE_NAME, and declare their types in src/vite-env.d.ts.

How do I type children in a React component?

Add children: React.ReactNode to the component’s props type. ReactNode covers elements, strings, numbers, arrays and null.

How do I add TypeScript to an existing React project?

Install TypeScript and the React types, add a tsconfig.json with allowJs on, and rename files to .ts/.tsx one at a time, adding types as you go.

Which React component library works best with TypeScript?

Most modern libraries ship full types, including MUI, Ant Design, Mantine and ReExt. Choose ReExt when you also need enterprise grids, forms and charts in the same library.

Can I use Ext JS components in a React TypeScript app?

Yes. ReExt 1.2 lets you use Ext JS 8.0 components inside React apps, with TypeScript support.

How long does it take to learn TypeScript for React?

Most React developers are productive within one to two weeks. The time pays for itself quickly in less debugging.

Key takeaways

  • A React + TypeScript app takes one command: npm create vite@latest my-app — –template react-ts.
  • Use Vite for single-page apps; use Next.js or React Router v7 when you need server rendering or full-stack features.
  • TypeScript is the default in every modern React template, and costs nothing at runtime.
  • Existing CRA projects can move to Vite in about a day.
  • For data-heavy screens, ReExt brings Ext JS 8.0‘s 140+ enterprise components, including high-performance grids, into React.

Ready to build data-heavy React screens faster? Start a free ReExt trial and drop an enterprise grid into the app you just created.

Sencha CTA Banner: Try Sencha ReExt

Author

Classic

Classic is an experienced software engineer with a strong background in web and mobile development, particularly in Javascript. He is highly skilled in this area and enjoys sharing his knowledge and expertise with others. Classic is passionate about software development and is always looking for ways to improve his skills and stay up-to-date with the latest technologies and best practices.

Start building with Ext JS today

Build 10x web apps faster with 140+ pre-build components and tools.