React and Vite Setup: Components and JSX

MERN Stack Full Course: MongoDB, Express, React and Node.js · React Fundamentals · 7 min read · Updated

From JSX to the screen

  1. In React you write the screen as components that return JSX, which looks like HTML inside JavaScript.
  2. Vite compiles the JSX and serves the app with instant reloads while you work.
  3. A component is a function. Components can contain other components, like Header and JobCard.
  4. React turns the components into elements in the page.
  5. Custom components start with a capital letter, and JSX needs one parent element.

The back end is finished enough to be useful. Now the screens. React is a library for building user interfaces out of small, reusable pieces called components. Instead of finding elements and changing them by hand, you describe what the screen should look like for the current data, and React updates the page to match. This is the biggest idea in React, and the next six lessons build on it.

We use Vite to run and build the app. It starts in a moment, reloads the browser as you edit, and produces a small, fast production build. Create React App is no longer the recommended way to start, and Vite is the common replacement.

The files Vite gives you

{
  "name": "job-tracker-client",
  "private": true,
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^19.2.0",
    "react-dom": "^19.2.0"
  },
  "devDependencies": {
    "@vitejs/plugin-react": "^5.1.1",
    "vite": "^7.2.4"
  }
}
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Job Tracker</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';
import './index.css';

// React takes over the <div id="root"> in index.html and draws our components inside it.
createRoot(document.getElementById('root')).render(
  <StrictMode>
    <App />
  </StrictMode>,
);
  • `index.html` is the only HTML page. It has an empty <div id="root"> and loads main.jsx as a module. Everything else is drawn by JavaScript.
  • `main.jsx` is the entry point. createRoot(...).render(<App />) tells React to take control of the root div and draw the App component inside it. StrictMode runs some checks in development (and runs effects twice on purpose, to help you find bugs).
  • `package.json` lists React, ReactDOM, the Vite plugin for React, and Vite. The scripts are dev, build and preview.

The proxy to your API

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

// In development, the React app runs on port 5173 and calls /api. Vite forwards those calls to the Express
// server on port 5000, so the browser sees ONE origin and we avoid CORS trouble while developing.
export default defineConfig({
  plugins: [react()],
  server: {
    port: 5173,
    proxy: { '/api': 'http://localhost:5000' },
  },
});

In development the React app runs on port 5173 and the API on 5000. A page on one port that calls another is a cross-origin request, which needs CORS. The proxy setting avoids that: Vite forwards any request that starts with /api to the Express server, so the browser sees only one address. Your React code can then call fetch('/api/jobs') with a plain path, and in production, when Express serves the built app (lesson 38), the same code works without any change.

Components and JSX

// A component is a function that returns JSX: HTML-like markup written inside JavaScript.
export default function Header() {
  const year = new Date().getFullYear(); // any JavaScript expression goes inside { }
  return (
    <header className="site-header">
      <strong>Job Tracker</strong>
      <span className="muted">Your search, in one place · {year}</span>
    </header>
  );
}

A component is a function that returns JSX. JSX looks like HTML but is JavaScript underneath: each tag becomes a function call. Four rules cover most of what you need.

  • A component's name starts with a capital letter (Header), which is how React tells it from an ordinary HTML tag (header).
  • A component returns one root element. Wrap siblings in a parent, or in an empty fragment <>...</>.
  • Use className instead of class, and htmlFor instead of for, because class and for are reserved words in JavaScript.
  • Put any JavaScript expression inside curly braces: {year}, {items.length}, {isOpen ? 'Hide' : 'Show'}. Statements such as if and for do not go in braces; use the conditional ? :, &&, or .map() for lists.
// For now this card always shows the same job. In lesson 26 it will receive the job as props.
export default function JobCard() {
  return (
    <article className="card">
      <h2>Dispatcher</h2>
      <p>Acme Logistics · Pune</p>
      <span className="badge">applied</span>
    </article>
  );
}
import Header from './components/Header.jsx';
import JobCard from './components/JobCard.jsx';

export default function App() {
  return (
    <>
      <Header />
      <main className="container">
        <h1>My jobs</h1>
        <JobCard />
        <JobCard />
      </main>
    </>
  );
}

App is the root component. It renders a Header, and two JobCards, using each like an HTML tag. Right now both cards show the same text. In the next lesson, props let each card receive its own data. Notice also how the layout is described, not constructed: there is no code that says “create an element and append it”.

The styles

:root { --bg:#f8fafc; --ink:#0f172a; --muted:#64748b; --line:#e2e8f0; --brand:#4f46e5; --brand-ink:#fff; }
* { box-sizing: border-box; }
body { margin:0; font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; background:var(--bg); color:var(--ink); line-height:1.5; }
.container { max-width: 860px; margin: 0 auto; padding: 16px; }
.site-header { display:flex; gap:12px; align-items:baseline; flex-wrap:wrap; padding:12px 16px; background:#fff; border-bottom:1px solid var(--line); }
.muted { color: var(--muted); font-size:.9rem; }
.card { background:#fff; border:1px solid var(--line); border-radius:12px; padding:14px 16px; margin:10px 0; }
.card h2 { margin:0 0 4px; font-size:1.1rem; }
.badge { display:inline-block; padding:2px 10px; border-radius:999px; background:#eef2ff; color:#3730a3; font-size:.8rem; }

We use one plain CSS file. The custom properties at the top (--brand and friends) make colours easy to change in one place. In lesson 34 the file is replaced by a polished responsive version.

Your task

  1. Run npm run setup from the project root (or npm install inside client), then npm run dev inside client. Open the address Vite prints.
  2. Change the text in Header.jsx and save. Watch the browser update without a reload.
  3. Create a Footer component that shows the year, and use it in App.
  4. Open the browser tools, Network tab, and find the request for main.jsx. See how Vite serves your source files in development.

How to check it worked

  • The browser shows the header and two cards.
  • Editing a component updates the page immediately (this is called hot reloading).

Summary

  • index.html + main.jsx start React; App is the root component.
  • A component is a capitalised function returning one JSX element; braces hold JavaScript expressions.
  • The Vite proxy sends /api calls to Express, so the same URLs work in development and production.

Download the solution (mern-lesson-25.zip)

Frequently asked questions

Why use Vite instead of Create React App?

Vite starts and reloads much faster and is actively maintained, while Create React App is no longer the recommended starting point. The React code you write is the same either way.

What is JSX?

A syntax that lets you write HTML-like markup inside JavaScript. A build step turns each tag into a function call. It is not required, but nearly every React project uses it.

Why does React run my effects twice in development?

StrictMode does it on purpose to help you find effects that are not cleaned up properly. It only happens in development, never in the production build.