First React Application and JSX

Introduction

Your first React app mounts a root component into the page and re-renders when state changes. This chapter covers main.tsx, JSX syntax, useState, a click counter, and hot module replacement (HMR) so edits appear instantly.

Prerequisites

Entry Point: main.tsx

src/main.tsx:

tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
 
createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <App />
  </StrictMode>
);

index.html:

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

Code explanation:

  • createRoot is the React 18+ API (replaces legacy ReactDOM.render)
  • StrictMode runs extra checks in development—effects may run twice on purpose
  • type="module" enables ES imports; Vite bundles for the browser

Root Component App.tsx

src/App.tsx:

Code explanation:

  • useState returns [value, setter]—calling setCount schedules a re-render
  • {count} embeds JavaScript expressions in JSX
  • onClick uses camelCase (not onclick)
  • className replaces HTML class
  • type="button" avoids accidental form submit behavior

Save the file—the browser updates without a full reload (HMR).

What Is JSX?

JSX looks like HTML inside JavaScript. Vite compiles it to React.createElement calls.

tsx
// JSX
const el = <h1 className="title">Hi</h1>;
 
// Rough compiled output
const el = React.createElement("h1", { className: "title" }, "Hi");

Rules:

HTMLJSX
classclassName
forhtmlFor
Self-closing tags required<img />, <input />
JavaScript expressionsWrap in { }

Use semantic HTML inside JSX—<main>, <button>, not clickable <div>s.

Fragments

A component must return one parent element. Use a Fragment to avoid extra DOM nodes:

tsx
return (
  <>
    <h1>Title</h1>
    <p>Paragraph</p>
  </>
);

Equivalent: <React.Fragment> or <Fragment> with import.

Exporting Components

StyleExample
Default exportexport default function App() — import App from "./App"
Named exportexport function Header() — import { Header } from "./Header"

This track uses default export for page-level components and named exports for small utilities—match your team convention.

Styling the Root

src/App.css:

css
.app {
  font-family: system-ui, sans-serif;
  max-width: 32rem;
  margin: 2rem auto;
  padding: 1rem;
}
 
button {
  padding: 0.5rem 1rem;
  cursor: pointer;
}

Global resets live in index.css. Styling React Components covers CSS Modules later.

Compare With Vue SFC

React App.tsxVue App.vue
MarkupJSX in return<template>
StateuseStateref
EventsonClick@click
Stylesimport './App.css'<style scoped>

FAQ

Why document.getElementById("root")!?

TypeScript thinks getElementById may return null. ! asserts the element exists—ensure index.html has <div id="root">.

Can I put logic before return?

Yes—hooks and helpers go at the top of the function body, before return.

Why StrictMode double effects?

Development-only check for unsafe side effects—see Effects and Strict Mode.

.tsx vs .jsx?

This track uses TypeScript.tsx for files with JSX.

Variable named React required?

With modern JSX transform, you often do not import React just for JSX—Vite handles it. You still import hooks: import { useState } from "react".

Next chapter?

JSX Lists, Keys, and Conditionals.