Learning path · Level 9 · Tutorial 1

Build a Local React App with TypeScript

Configure React in a local TypeScript and Vite project, render a semantic interface with JSX, and extend the existing checking workflow to cover TSX and official React Hooks rules.

  • Level: Intermediate
  • Duration: 25 min
  • 8 steps
Start tutorial Back to the path
Build a Local React App with TypeScript

What you will go through

  1. Add React to the Local Vite Workflow Install React and its local development tooling, then register Vite's React integration while preserving the existing workflow. 3 min
  2. Enable Strict Checking for TSX Configure TypeScript to recognize and strictly check TSX source while preserving the existing local browser workflow. 3 min
  3. Mount the First Function Component Connect the HTML entry point to a safely mounted React function component. 4 min
  4. Build the Semantic JSX Structure 3 min
  5. Add Expressions and Native Attributes Render component-local values in JSX, use React DOM attribute conventions, and retain native label and input behavior. 3 min
  6. Extend ESLint Coverage to React Source Extend the local ESLint flat configuration so it checks React TSX source while preserving JavaScript and TypeScript coverage. 3 min
  7. Enable and Verify the Official Hooks Rules 3 min
  8. Verify the Complete Local React Workflow Verify the rendered Study Desk interface and collect end-to-end evidence that TypeScript, ESLint, and the official Hooks rules cover the local React source. 4 min

What you will learn

  • Configure local React dependencies, Vite integration, and TypeScript support for TSX.
  • Mount a function component and render semantic JSX containing JavaScript expressions.
  • Run type-check and lint commands that include TSX files.
  • Verify that the ESLint configuration enables the official Hook-placement and Effect-dependency rules.

Before you start

  • Run a Local Development Server with Vite
  • Enable Strict Type Checking in a Local Vite Project
  • Check JavaScript with ESLint
  • Organize Content with Semantic HTML
  • Build Accessible Forms with Native Validation

Step 1 of 8

Add React to the Local Vite Workflow

Install React and its local development tooling, then register Vite's React integration while preserving the existing workflow.

See React's place in the workflow

From manual DOM updates to declared UI

React changes how an interface is produced. Instead of application code selecting and manually replacing DOM nodes in an interface subtree, a component describes the intended interface output. React coordinates the corresponding DOM work.

In this tutorial, you will build a self-contained local workspace named study-desk. Vite remains the local development tool, while TypeScript and ESLint remain separate checking tools. Adding React extends that familiar workflow rather than replacing it.

How the local workflow fits together

The local workflow has distinct responsibilities: source code describes the interface, Vite serves and transforms it during development, React coordinates the interface output in the browser.

Diagram showing application source flowing through Vite to a browser, with React coordinating the displayed interface structure.

Your source passes through Vite to the browser. React describes and coordinates the application interface; Vite remains the local development tool.

Create the local workspace

Create the workspace

From a parent folder, create the local workspace and initialize its package metadata. The mkdir and cd commands do not print output; npm init -y writes the initial package.json.

Initialize study-desk

Run these commands from a parent folder.

bash
mkdir study-desk
cd study-desk
npm init -y
Expected output
Wrote to /path/to/study-desk/package.json:

{
  "name": "study-desk",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

The absolute path and default metadata written by npm can vary. The folder-creation and directory-change commands normally produce no output; the shown output is from `npm init -y`.

Install the project-local dependencies

Install the baseline development tools

Install the established Vite, TypeScript, and ESLint baseline as development dependencies. globals is included explicitly because the later flat ESLint configuration imports it directly. npm records every installation in package.json and creates or updates package-lock.json; keep that lockfile in the project.

Install Vite, checking tools, and browser globals

Run this inside the study-desk folder.

bash
npm install --save-dev vite typescript eslint @eslint/js typescript-eslint globals
Expected output
added 120 packages, and audited 121 packages in 4s

24 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

The number of added packages, audit total, timing, funding notices, and vulnerability result vary by package versions and date. npm should finish successfully and update both package.json and package-lock.json.

Add React and its development support

Now add React's browser runtime packages, followed by the TypeScript declarations and Vite integration that support React development. Runtime packages are used by the browser application; development dependencies support typing or Vite processing.

Install React runtime packages

Install the browser runtime packages.

bash
npm install react react-dom
Expected output
added 3 packages, and audited 124 packages in 1s

24 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

Exact package counts, timing, and audit messages vary. This command adds `react` and `react-dom` as dependencies.

Install React development packages

Install the React declarations and Vite integration.

bash
npm install --save-dev @types/react @types/react-dom @vitejs/plugin-react
Expected output
added 48 packages, and audited 172 packages in 3s

26 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

The exact package count, timing, funding notices, and audit result vary by package versions and date. The Vite React plugin brings several transitive development dependencies.

Keep the workflow responsibilities clear

Set the project commands

Set the project to use ES modules and define the three separate commands used throughout this tutorial. The development server, type checker, and linter have different jobs, so keep their scripts separate.

Configure package metadata and scripts

Run this command in study-desk. It replaces npm's default test script with the required scripts.

bash
npm pkg set type=module scripts.dev="vite" scripts.typecheck="tsc --noEmit" scripts.lint="eslint ."
npm pkg delete scripts.test

Required package.json essentials

Verify that package.json includes this metadata and these scripts. Dependency versions are managed by npm and are not shown here.

json
{
  "type": "module",
  "scripts": {
    "dev": "vite",
    "typecheck": "tsc --noEmit",
    "lint": "eslint ."
  }
}

Separate runtime and development responsibilities

React supplies the component API. React DOM is the browser-specific renderer that will connect the application to the page in a later step. The @types packages provide TypeScript declarations, while the Vite plugin lets Vite process React source.

Dependency roles

React-related package roles in study-desk.

React packages and their responsibilities
PackageDependency kindRole
reactRuntimeCore API for component output
react-domRuntimeBrowser DOM renderer for React output
@types/reactDevelopmentTypeScript declarations for React
@types/react-domDevelopmentTypeScript declarations for React DOM
@vitejs/plugin-reactDevelopmentVite integration for React source

Connect Vite to React source

Register Vite's React integration

Create vite.config.js with this complete configuration. Because package.json contains "type": "module", this configuration uses ES module imports.

The plugin belongs in Vite's plugins array. It adds React-aware source processing while npm run dev still starts Vite's local development server. The plugin does not render an application into the browser; the browser connection is added later.

vite.config.js

Create vite.config.js. This is the project's single Vite configuration file.

javascript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
});

Tip

What is ready now

At this point, study-desk has its local dependencies, lockfile, scripts, and Vite integration. The next step configures TypeScript to check React's TSX source format; a later step supplies the browser entry and first component.

Practice: identify the packages

Check dependency roles

Match every React-related package to its primary role.

Tap an item and then its matching pair.

Apply the integration

Complete the Vite plugin configuration

Complete the missing parts of vite.config.js using the project's single-quote convention. This registers the React integration with Vite; npm run dev still starts Vite's local development server.

javascript
import { defineConfig } from 'vite';
import react from ;

export default defineConfig({
  plugins: [()],
});

Step 2 of 8

Enable Strict Checking for TSX

Configure TypeScript to recognize and strictly check TSX source while preserving the existing local browser workflow.

Recognize TSX and its separate checks

Choose the right source extension

A .tsx file is TypeScript source that contains JSX. Keep ordinary TypeScript without JSX in .ts files. This distinction lets TypeScript parse each source file correctly while preserving the project’s existing TypeScript coverage.

Set "jsx": "react-jsx" to use React’s automatic JSX runtime. JSX in a .tsx file does not require a default React import just to compile.

Two related local workflows

Diagram showing TypeScript and TSX source files, TypeScript checking, Vite transformation, and browser output.

TSX is checked by TypeScript. Vite transforms and serves the source for the browser, but that browser path is separate from an explicit type-check.

Keep both source patterns covered

File extension and responsibility

Both extensions belong in the TypeScript source inclusion list.
Source fileContains JSX?Use extension
Utility or configuration-free browser logicNo.ts
React interface sourceYes.tsx

Extend tsconfig.json without weakening it

Update the existing TypeScript configuration

Update tsconfig.json by adding the JSX setting and extending include. Keep the established strict, browser-oriented options: this is an extension of the local TypeScript configuration, not a replacement for it.

Keep strict checking while adding TSX

tsconfig.json

json
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "allowImportingTsExtensions": true,
    "noEmit": true,
    "strict": true,
    "skipLibCheck": true,
    "jsx": "react-jsx"
  },
  "include": ["src/**/*.ts", "src/**/*.tsx"]
}

Tip

Preserve the existing local workflow

noEmit keeps npm run typecheck as a checking command: it reports diagnostics without generating JavaScript files. moduleResolution: "Bundler" and allowImportingTsExtensions: true remain in place, so later local imports can retain their explicit TypeScript extensions.

Practice the configuration change

Complete the TSX configuration patterns

Complete the JSX compiler mode and both source inclusion patterns.

json
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "allowImportingTsExtensions": true,
    "noEmit": true,
    "strict": true,
    "skipLibCheck": true,
    "jsx": ""
  },
  "include": ["", ""]
}

Distinguish serving from type-checking

Verify the purpose of type-checking

Why does a page loading through Vite not, by itself, prove that its TypeScript is valid?

Step 3 of 8

Mount the First Function Component

Connect the HTML entry point to a safely mounted React function component.

Trace the mount path

One route from HTML to React

React starts from one mount point. The module script in index.html loads src/main.tsx. That file finds the #root element, creates one React root, and renders the capitalized App function component into it.

React owns the subtree inside #root. Replace the earlier browser-entry code rather than keeping manual DOM rendering beside React. StrictMode enables development checks; it does not create a visible HTML element.

How the initial render reaches the browser

A flow diagram showing an HTML document leading through a TypeScript entry file and React component to a browser heading.

The HTML mount area leads to the TypeScript entry module, which renders the App component into the browser.

Replace the browser entry

index.html

Replace the previous browser entry in index.html. The module script now points to the TSX entry file, and the empty root element is React's mount location.

html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Study Desk</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

src/App.tsx

Create the initial application component in src/App.tsx. Component names begin with a capital letter, and this function returns JSX.

tsx
export default function App() {
  return <h1>Study Desk</h1>;
}

src/main.tsx

Create src/main.tsx. The null guard narrows the DOM lookup before React receives the element. Keep one root creation and wrap the application in StrictMode.

tsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.tsx';

const rootElement = document.getElementById('root');

if (!rootElement) {
  throw new Error('Root element not found.');
}

createRoot(rootElement).render(
  <StrictMode>
    <App />
  </StrictMode>
);

Start the local server

Save the three files, then start the existing local development workflow. Open the Local address shown by Vite. The browser should display the Study Desk heading.

bash
npm run dev
Expected output
> study-desk@1.0.0 dev
> vite

  VITE v6.0.0  ready in 180 ms

  ➜  Local:   http://localhost:5173/

The Vite version, startup time, and local port can vary. Use the Local address printed in your terminal.

Check the safe sequence

Order the mount sequence

Put the mounting operations in the order used by main.tsx.

  1. Throw an error if the element is missing.
  2. Find the element with id root.
  3. Create the React root and render App inside StrictMode.

Apply and verify locally

Complete the safe mount

Use the partial main.tsx code below to rehearse the three essential operations. Then apply the completed version in your local file and refresh the browser running your Vite server to confirm that Study Desk appears.

Mount App safely

Fill the three missing parts of this mounting excerpt.

tsx
const rootElement = document.('root');

if () {
  throw new Error('Root element not found.');
}

createRoot(rootElement).(
  <StrictMode>
    <App />
  </StrictMode>
);

Step 4 of 8

Build the Semantic JSX Structure

Visualize the semantic hierarchy

Map the page structure to JSX

JSX can express the same semantic structure you already use in HTML. In App, the page will have a main area containing a heading and a section, followed by a sibling footer.

Each opened JSX element must be closed, and child elements must close before their parent closes. This nesting is part of both the interface meaning and the structure React renders.

From JSX hierarchy to browser structure

A tree diagram showing main and footer as siblings, with a heading and section nested inside main, and a fragment grouping the siblings without adding a visible node.

The fragment groups the returned siblings for React, while main and footer remain the elements that appear in the DOM.

Write the first semantic component

Use a fragment for sibling elements

A component must return one JSX expression. Here, a fragment (<>...</>) groups main and footer without adding an unnecessary wrapper element to the browser DOM.

Lowercase names such as main, section, and footer are native browser elements. A capitalized name such as App refers to a React component. Void native elements have no closing tag; write them with self-closing syntax, such as <hr />.

src/App.tsx

In src/App.tsx, replace the component body with this semantic structure.

tsx
export default function App() {
  return (
    <>
      <main>
        <h1>Study Desk</h1>
        <section>
          <h2>Next session</h2>
          <p>Prepare your desk for focused local practice.</p>
        </section>
      </main>
      <footer>
        <p>Built locally with React.</p>
      </footer>
    </>
  );
}

Example

Why not add a wrapper?

The fragment is not a visible HTML element. It solves the single-return-expression requirement while preserving main and footer as siblings.

Practice balanced nesting

Repair the JSX structure

Complete the two missing closing tags so the JSX nesting is valid.

tsx
export default function App() {
  return (
    <>
      <main>
        <h1>Study Desk</h1>
        <section>
          <h2>Next session</h2>
          <p>Prepare your desk.</p>
        </section>
      
      <footer>
        <p>Local React practice</p>
      </footer>
    
  );
}

Identify the DOM result

Check the rendered result

After this component renders, what does the fragment add to the browser DOM around main and footer?

Step 5 of 8

Add Expressions and Native Attributes

Render component-local values in JSX, use React DOM attribute conventions, and retain native label and input behavior.

Render component values

Render values with JSX expressions

To render a component-local value as JSX text, place a JavaScript expression inside braces: {topic} or {sessionMinutes + 5}.

Braces accept expressions that produce a value. They do not accept statements such as const total = 25;. Declare constants in the component body before return, then read them from the JSX you return.

From constants to browser output

Expressions calculate or read a value; statements structure the program outside returned JSX.

Use braces when JSX needs the result of an expression.
Inside JSX bracesKindResult
`topic`ExpressionReads a component-local value
`sessionMinutes + 5`ExpressionCalculates a value to render
`const total = 25;`StatementDeclare it before `return` instead

Expressions and native controls

Diagram showing component constants flowing into rendered text and a label directing focus to a text input.

JSX expressions supply displayed values, while native label and input semantics remain available in the browser.

Complete the semantic page

Use React DOM attribute names

Replace src/App.tsx with this component. The constants stay inside App, and braces insert their values into the paragraph.

React DOM uses className and htmlFor because these JSX properties map to native HTML attributes. Native aria-* and data-* names remain hyphenated. The input has no value, defaultValue, or event handler, so it remains a native, unbound text input.

Study Desk component

src/App.tsx

tsx
export default function App() {
  const topic = 'React with TypeScript';
  const sessionMinutes = 25;

  return (
    <>
      <main className="study-desk">
        <h1>Study Desk</h1>
        <section
          className="session-card"
          aria-labelledby="session-heading"
          data-section="next-session"
        >
          <h2 id="session-heading">Next session</h2>
          <p>{topic} · {sessionMinutes} minutes</p>
          <label htmlFor="session-note">Session note</label>
          <input id="session-note" name="sessionNote" type="text" />
        </section>
      </main>
      <footer>
        <p>Plan one focused session at a time.</p>
      </footer>
    </>
  );
}

Tip

Keep the native association

Keep the visible label and the input identifier identical. Clicking the label then gives the native text input focus without any React event code.

Style the local interface

Load the stylesheet

Load the local stylesheet from the existing entry file. Add this import alongside the other imports in src/main.tsx. Keep the safe root lookup and React mounting code from the previous step unchanged.

Style import

Add to src/main.tsx

typescript
import './style.css';

Small local stylesheet

src/style.css

css
:root {
  font-family: system-ui, sans-serif;
  color: #172033;
  background: #f4f7fb;
}

body {
  margin: 0;
}

.study-desk,
footer {
  width: min(42rem, calc(100% - 2rem));
  margin: 0 auto;
}

.study-desk {
  padding: 3rem 0 1rem;
}

.session-card {
  padding: 1.5rem;
  border-radius: 0.75rem;
  background: #ffffff;
  box-shadow: 0 0.25rem 1rem rgb(23 32 51 / 12%);
}

label,
input {
  display: block;
}

label {
  margin-top: 1rem;
  font-weight: 700;
}

input {
  width: 100%;
  box-sizing: border-box;
  margin-top: 0.4rem;
  padding: 0.65rem;
}

footer {
  padding-bottom: 2rem;
  color: #526079;
}

Complete and verify

Complete the JSX excerpt

Complete the component values, the JSX expressions, and the label association properties.

tsx
const topic = ;
const sessionMinutes = ;

<p> ·  minutes</p>
<label ="session-note">Session note</label>
<input ="session-note" name="sessionNote" type="text" />

Verify in your browser

Apply the completed component in src/App.tsx, then run npm run dev. In one or two sentences, report whether the session line matches the component constants and what happens when you click the visible Session note label.

Write at least 20 characters (0/20).

Step 6 of 8

Extend ESLint Coverage to React Source

Extend the local ESLint flat configuration so it checks React TSX source while preserving JavaScript and TypeScript coverage.

Keep the checking responsibilities separate

Expand coverage without replacing the workflow

Your existing local workflow stays in place. The change is in ESLint’s flat configuration: it must discover JavaScript, TypeScript, and TSX source without scanning generated output.

A .tsx file is still source code for ESLint. Vite serves the app, TypeScript checks types, and ESLint checks source-quality rules; run each command for its own purpose.

What the lint configuration should reach

A diagram showing ESLint reaching JavaScript, TypeScript, and TSX source files while skipping the dist output folder.

The source patterns include authored JavaScript, .ts, and .tsx files. The global ignore keeps generated dist output out of linting.

Three local commands, three jobs

Keep these commands separate, even when you run them one after another during development.

Each command checks a different part of the local workflow.
CommandPrimary jobDoes not replace
npm run devServe the Vite application locallyType and lint checks
npm run typecheckCheck TypeScript without emitting filesServing and lint rules
npm run lintCheck configured ESLint rulesServing and TypeScript checking

Scope ESLint to all authored source

Extend the current flat configuration

Edit the existing eslint.config.js; do not replace it with a separate React starter configuration. This version preserves authored JavaScript coverage, extends the TypeScript scope to TSX, supplies browser globals to browser source, and globally ignores generated output.

The typescript-eslint recommended configuration supplies TypeScript-aware parsing and recommended rules for the .ts and .tsx scope.

eslint.config.js

Replace the existing flat configuration with this scoped version.

javascript
import js from '@eslint/js';
import globals from 'globals';
import tseslint from 'typescript-eslint';

export default tseslint.config(
  {
    ignores: ['dist/**']
  },
  {
    files: ['**/*.{js,mjs,cjs}'],
    extends: [js.configs.recommended],
    languageOptions: {
      globals: globals.browser
    }
  },
  {
    files: ['**/*.{ts,tsx}'],
    extends: [js.configs.recommended, ...tseslint.configs.recommended],
    languageOptions: {
      globals: globals.browser
    }
  }
);

package.json essentials

Keep existing dependency entries. Ensure the project remains an ES module and that its scripts include this exact set.

json
{
  "type": "module",
  "scripts": {
    "dev": "vite",
    "typecheck": "tsc --noEmit",
    "lint": "eslint ."
  }
}

Prove that TSX is being linted

Create a short TSX lint probe

A temporary known violation proves that ESLint reaches App.tsx; a clean result alone would not prove that the file was discovered. Inside App, directly after the two existing local constants, add this one temporary statement. Do not leave it in the application.

Deliberate violation

Temporary statement inside src/App.tsx

typescript
debugger;

Expected lint diagnostic

Run the lint script while the temporary statement is present.

shell
npm run lint
Expected output
> study-desk@1.0.0 lint
> eslint .

<project-path>/study-desk/src/App.tsx
  4:3  error  Unexpected 'debugger' statement  no-debugger

✖ 1 problem (1 error, 0 warnings)

The absolute path varies by computer. The important evidence is that the diagnostic names src/App.tsx and the no-debugger rule.

Restore the clean source

Now delete only the temporary debugger; statement and run npm run lint again. A clean run has no ESLint diagnostics, confirming that the project is restored.

Apply and verify the coverage change

Run the complete local lint check

On your own computer, complete this acceptance check:

  1. Confirm that eslint.config.js uses **/*.{js,mjs,cjs} for JavaScript, **/*.{ts,tsx} for TypeScript and TSX, and a global dist/** ignore.
  2. Confirm that the lint script is eslint ..
  3. Add the temporary debugger; line in App.tsx, run npm run lint, and observe the no-debugger diagnostic for that TSX file.
  4. Remove the line and run npm run lint again with no diagnostics.

Report what you observed and distinguish the jobs of npm run dev, npm run typecheck, and npm run lint.

Write at least 180 characters (0/180).

Step 7 of 8

Enable and Verify the Official Hooks Rules

Apply the preset without losing coverage

Add the official Hooks preset

Install the official plugin, then add one focused configuration layer. Keep the JavaScript and TypeScript layers from the previous step: they continue to cover authored JavaScript, .ts, and .tsx files. The new layer applies only to React source under src.

This preset prepares checks that later lessons will use. You do not need to write or call any Hooks now.

Install the Hooks plugin

Run this in the study-desk folder.

shell
npm install --save-dev eslint-plugin-react-hooks

eslint.config.js

Replace eslint.config.js with this complete flat configuration. The final scoped object extends the official recommended preset without replacing the existing JavaScript or TypeScript coverage.

javascript
import js from '@eslint/js';
import globals from 'globals';
import reactHooks from 'eslint-plugin-react-hooks';
import tseslint from 'typescript-eslint';

export default tseslint.config(
  {
    ignores: ['dist/**']
  },
  {
    files: ['**/*.{js,mjs,cjs}'],
    extends: [js.configs.recommended],
    languageOptions: {
      globals: globals.browser
    }
  },
  {
    files: ['**/*.{ts,tsx}'],
    extends: [js.configs.recommended, ...tseslint.configs.recommended],
    languageOptions: {
      globals: globals.browser
    }
  },
  {
    files: ['src/**/*.{ts,tsx}'],
    extends: [reactHooks.configs.flat.recommended]
  }
);

Tip

Keep the preset intact

reactHooks.configs.flat.recommended provides the plugin and its recommended rules together. Leave those recommended rules intact. Later tutorials will show how the checks respond to real Hook code.

Verify the rules that apply to TSX

Inspect the effective TSX configuration

A successful npm run lint means the rules currently applied found no violations. It does not prove that every possible rule is enabled. App.tsx has no Hook calls yet, so inspect ESLint’s resolved configuration for that specific TSX file to verify the official checks directly.

Resolved configuration versus a lint run

Diagram showing a flat ESLint configuration resolving into rules for a highlighted TSX application file.

The resolved configuration answers which rules apply to this file; a lint run answers whether the current code violates them.

The two required rule keys

The first number in a resolved rule entry is its severity. Any nonzero severity means the rule is enabled.

Typical severities from the official recommended preset
Resolved rule keyWhat it checksTypical severity
react-hooks/rules-of-hooksHook placement2 (error)
react-hooks/exhaustive-depsEffect dependencies1 (warning)

Print the configuration for App.tsx

After saving the plugin configuration, run this command from the study-desk folder.

shell
npx eslint --print-config src/App.tsx
Expected output
{
  "rules": {
    "react-hooks/rules-of-hooks": [
      2
    ],
    "react-hooks/exhaustive-deps": [
      1
    ]
  }
}

The command prints a complete JSON configuration whose other entries vary with installed package versions. This is the relevant excerpt: both rule keys have nonzero severities.

Choose the right evidence

Choose the direct evidence

Why is npm run lint alone insufficient evidence that the two official Hooks rules are enabled for src/App.tsx?

Local verification

Verify the active Hooks rules

In your local study-desk project, install the plugin and apply the complete configuration from this step. Then run npx eslint --print-config src/App.tsx.

Report all four pieces of evidence from your local result:

  1. the plugin import name;
  2. the preset path applied to src/**/*.{ts,tsx};
  3. the resolved severity for react-hooks/rules-of-hooks;
  4. the resolved severity for react-hooks/exhaustive-deps.

Report each severity as the first number in its resolved rule entry. Do not use only a successful lint run as evidence.

Write at least 90 characters (0/90).

Step 8 of 8

Verify the Complete Local React Workflow

Verify the rendered Study Desk interface and collect end-to-end evidence that TypeScript, ESLint, and the official Hooks rules cover the local React source.

1. Confirm the browser result

Check the rendered interface

End-to-end browser check

Work in your local study-desk folder. In src/App.tsx, confirm that the component-local values are restored to the required final values. Then start the application with npm run dev and open the local address Vite reports.

Check the rendered page, not just the source code:

  • Study Desk is the main heading.
  • The Next session section shows the topic and duration from the constants.
  • Clicking the visible Session note label moves focus to its native text input.

This is an acceptance check of the existing application. Do not add state, handlers, or extra components.

Restore the final values

Required component-local values in src/App.tsx

tsx
const topic = 'React with TypeScript';
const sessionMinutes = 25;

What to inspect in the browser

A browser page diagram showing a main heading, a session section, a label associated with a text input, and a footer.

The visible interface should preserve semantic structure while the label activates the native input.

2. Check, then restore, TSX

Use a reversible type-error probe

Prove that TSX is checked

A page that renders can still contain a type error. Make a temporary, deliberate error in src/App.tsx by replacing the duration declaration with the line below. Save the file, run npm run typecheck, and observe the diagnostic.

Immediately restore the required numeric declaration from the previous screen. Run npm run typecheck again. A clean run has no TypeScript diagnostics.

This probe is only evidence that the checker reaches TSX; it must not remain in the project.

Intentional TSX error

Temporary replacement in src/App.tsx

tsx
const sessionMinutes: number = 'twenty-five';

Type-check command

Run while the temporary replacement is present

shell
npm run typecheck
Expected output
> study-desk@1.0.0 typecheck
> tsc --noEmit

src/App.tsx(4,7): error TS2322: Type 'string' is not assignable to type 'number'.

The path prefix and line number can differ on your computer. The important evidence is a TS2322 diagnostic referring to `src/App.tsx`.

Tip

Restore before continuing

After restoring const sessionMinutes = 25;, run npm run typecheck once more. Record that the restored project is clean before continuing to lint.

3. Verify type, lint, and Hooks coverage

Inspect each checker independently

Gather coverage evidence

Keep the tools separate:

  • npm run typecheck checks TypeScript without emitting files.
  • npm run lint checks the authored files ESLint discovers.
  • --print-config shows the effective ESLint configuration for one target file.

Use the file-list command to confirm that both src/App.tsx and src/main.tsx are included by TypeScript. Check effective ESLint configuration for App.tsx, main.tsx, and the authored JavaScript Vite configuration. A clean lint command should report no diagnostics.

Coverage commands

TypeScript file coverage and a clean lint run

shell
npm run typecheck -- --listFilesOnly
npm run lint
Expected output
/your-path/study-desk/src/App.tsx
/your-path/study-desk/src/main.tsx

> study-desk@1.0.0 lint
> eslint .

The TypeScript list also contains many TypeScript library files and uses machine-specific absolute paths. The excerpt below shows the two relevant application files. A clean lint run may display only the npm script header.

ESLint configuration evidence

Inspect resolved ESLint configurations

shell
npx eslint --print-config src/App.tsx
npx eslint --print-config src/main.tsx
npx eslint --print-config vite.config.js
Expected output
{
  "rules": {
    "react-hooks/rules-of-hooks": [2],
    "react-hooks/exhaustive-deps": [1]
  }
}

Each command prints a large JSON object. Inspect the `rules` object in the App configuration. The excerpt shows the relevant Hooks entries; other resolved settings and formatting can vary. The other two commands provide evidence that `.tsx` and authored `.js` targets are still covered.

Tip

Read the Hooks evidence

For src/App.tsx, verify that both exact rule keys appear with a nonzero severity. Typically, rules-of-hooks is 2 and exhaustive-deps is 1. This resolved configuration—not merely a clean component with no Hooks—is the evidence that the official checks remain active.

4. Diagnose and report the complete workflow

Final diagnostic

Match each observed failure to the configuration area most directly responsible.

Tap an item and then its matching pair.

Submit the complete workflow report

Submit one short acceptance report after completing the diagnostic. Include: the rendered topic and duration; what happened when you clicked Session note; the result after restoring the temporary type probe and running both check commands; evidence that App.tsx and main.tsx were listed; evidence that ESLint also resolved configuration for a .tsx file and authored JavaScript file; and the enabled severities you observed for both Hooks rules.

Write at least 250 characters (0/250).

Summary

Workflow complete

You now have a complete local baseline for later React work.

  • Vite serves the mounted semantic Study Desk interface in the browser.
  • TypeScript checks both TSX entry and component files through an explicit type-check command.
  • ESLint checks authored JavaScript and TypeScript/TSX files through a separate lint command.
  • Resolved ESLint configuration confirms that the official Hook-placement and Effect-dependency rules are enabled.

Complete

Congratulations! You completed: Build a Local React App with TypeScript

Completed: you verified a locally rendered React TypeScript interface and the explicit checks that protect its source.

Download the App now to have access to + 5000 free courses, exercises, certificates and lots of content without paying anything!

  • 100% free online courses from start to finish

    Thousands of online courses in video, ebooks and audiobooks.

  • More than 60 thousand free exercises

    To test your knowledge during online courses

  • Valid free Digital Certificate with QR Code

    Generated directly from your cell phone's photo gallery and sent to your email

Cursa app on the ebook screen, the video course screen and the course exercises screen, plus the course completion certificate