← React / TypeScript

React / TypeScript · 10 / 18

First React app: JSX, props and state

Render components and change state through a user action.

  • Medium
  • Basics
  • 45 min

Prerequisites: Promises, async/await and errors

React describes interfaces as components. Here, a component is a function returning JSX. JSX resembles HTML but appears inside JavaScript or TypeScript. Props are inputs to a component. State is its mutable state. When state changes, React calculates the display again. We build a counter with a separate display component.

01Prepare the learning project

Create a new folder and save all four sample files at their specified paths. npm install installs the pinned dependencies. npm run dev starts Vite. Open the address displayed in the terminal. Use Node.js 22.13 or newer.

package.json · jsonOpen file
{
  "name": "react-learning",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite --host 127.0.0.1",
    "build": "vite build",
    "test": "vitest run"
  },
  "dependencies": {
    "react": "19.2.3",
    "react-dom": "19.2.3"
  },
  "devDependencies": {
    "vite": "8.1.5",
    "typescript": "6.0.3",
    "@types/react": "19.2.14",
    "@types/react-dom": "19.2.3",
    "vitest": "4.1.10",
    "@testing-library/react": "16.3.2",
    "@testing-library/user-event": "14.6.1",
    "jsdom": "29.1.1"
  }
}

Run / check

npm install
npm run dev

02Read JSX and props

Count expects value as a number. <Count value={count} /> passes the current value. Curly braces insert a TypeScript expression into JSX. The function does not need to know how the value changes.

src/App.tsx · tsxOpen file
import React, { useState } from 'react';
function Count({ value }: { value: number }) { return <p>Entries: {value}</p>; }
export function App() {
  const [count, setCount] = useState(0);
  return <main>
    <h1>House notes</h1>
    <Count value={count} />
    <button onClick={() => setCount(previous => previous + 1)}>Add entry</button>
  </main>;
}

03Update state

useState returns the value and an update function. setCount(previous => previous + 1) calculates the new value from the latest state. count++ would be a hidden mutation and would not reliably update React.

04Trace responsibility

main.tsx connects React to the root DOM node. App owns the counter. Count only displays it. The real projects add routing, domain services and persistence. This counter deliberately stays in memory.

Expected result

Add entry increments Entries: 0 by 1 each time. Reloading resets the counter to 0.

Your exercise

Add a reset button. Should Count own this action?

Show the solution and explanation

The button belongs to App and calls setCount(0). Count still receives only value. This keeps display and changes separate. If Count later displays a button, the action can be passed as a callback prop.

Project context and sources

The example is simplified for learning. The linked source version shows its use in the project and the checks applied there.