React for People Who Are Not Frontend Developers

9 minute read

I got dropped into an active frontend project. My background is backend and infrastructure. I had never written a React component in my life. The codebase had hundreds of .tsx files and I had no idea where to start.

This post is what I wish someone had handed me on day one. It is not a React tutorial. It is a survival guide for reading and navigating an existing React project when you already know how to code, just not in this world.


What Vite Is (and Why You Can Mostly Ignore It)

The project uses Vite. Before anything else, here is the mental model that made it click for me.

Vite is not React. It is not a framework. It is the tooling that sits around your frontend code and does two jobs:

npm run dev    -> starts a local server, watches for changes,
                  reloads the browser when you save a file

npm run build  -> compiles everything into a dist/ folder
                  that a browser can actually run

That is it. You do not need to become a Vite expert. The four things you actually need to know about it:

package.json    -> lists dependencies and defines the commands
vite.config.ts  -> configures how Vite builds the project
.env files      -> environment variables (API URLs, feature flags)
dist/           -> the output folder after npm run build

When you type npm run dev, npm reads package.json and runs vite. When you type npm run build, it runs vite build. That is the entire relationship between npm and Vite.

The rest of what you need to understand is React — and one more layer that sits on top of it.


UI Libraries: The Furniture Inside the House

Vite does not care what UI library you use. React does not either. They are independent layers.

Keep this separation in your head:

React
  |
  +-- UI libraries       MUI, Ant Design, Bootstrap
  |
  +-- Build/dev tool     Vite

So what is a UI library?

Without one, you build every button, table, and dropdown yourself:

<button className="my-button">Save</button>

And you write all the CSS yourself. That works, but it is a lot of work.

A UI library gives you pre-built, pre-styled components:

// MUI
<Button variant="contained">Save</Button>

// Ant Design
<Button type="primary">Save</Button>

Same result in the browser. No CSS to write. The library handles the visual design.

The analogy that stuck for me:

React              = building the house (structure, rooms, wiring)
MUI / antd         = buying ready-made furniture (buttons, tables,
                     dropdowns, dialogs, date pickers, icons)
Vite               = the tools used to build and run the house

UI libraries give you ready-made versions of:

Component What it replaces
Button <button> with custom CSS
Table <table> with sorting, pagination built in
Dialog / Modal Custom overlay and backdrop code
Select / Dropdown <select> with search and multi-select
DatePicker A full calendar widget from scratch
Icons SVG files or icon fonts

When you open an unfamiliar project and see imports like:

import { Button, Table, Dialog } from '@mui/material';
import { Button, Table, Modal } from 'antd';

Those are UI library components. They behave like any other React component — they accept props, they fire events, they render HTML. The only difference is someone else wrote and styled them.

If a component is behaving unexpectedly, check whether it comes from a UI library. The props it accepts are documented on that library’s website, not in your codebase.


The Project Structure

A typical Vite + React + TypeScript project looks like this:

my-app/
  package.json       <- dependencies and commands
  vite.config.ts     <- Vite configuration
  index.html         <- browser starts here
  .env               <- environment variables
  src/
    main.tsx         <- React starts here
    App.tsx          <- root component
    components/      <- reusable UI pieces
    pages/           <- full page components
  public/            <- static files (images, fonts)
  dist/              <- built output (do not edit this)

The startup chain is worth memorising. When a browser loads the app, this is the order things run:

Browser loads index.html
        |
index.html loads src/main.tsx
        |
main.tsx renders <App />
        |
App.tsx renders your page components
        |
Page components render smaller components

So if you want to understand what the app does, start at App.tsx and follow the tree downward.


The Five React Concepts You Actually Need

Component

A component is a function that returns UI. That is the whole definition.

function AgentTable() {
  return <h1>Agents</h1>;
}

A page is a tree of components nested inside each other:

App
  Header
  Sidebar
  Dashboard
    FilterBar
    AgentTable
      AgentRow
      AgentRow
      AgentRow

When you see <AgentTable /> in code, read it as: “put the AgentTable component here.” The angle brackets are not HTML — they are React’s way of calling a component function.

Props

Props are inputs to a component. They work exactly like function arguments.

// parent passes agents down
<AgentTable agents={agents} />

// child receives it
function AgentTable({ agents }) {
  ...
}

Data flows one direction: parent to child. If you see a component receiving data, look upward in the tree to find where that data comes from.

Dashboard
    |
    | agents (prop)
    v
AgentTable

State

State is a component’s memory. It is a value that, when changed, causes React to redraw the UI.

const [agents, setAgents] = useState([]);

Read that line as three things:

agents       -> current value (starts as empty array)
setAgents    -> function to change the value
useState([]) -> initial value

When you call setAgents(newData), two things happen: the value updates, and React redraws every part of the UI that uses agents. This is the core mechanic of React. Everything else builds on it.

setAgents(data)
      |
state changes
      |
React redraws UI

Events

Events connect user actions to functions.

<button onClick={handleFilter}>Filter</button>
<input  onChange={handleSearch} />
<form   onSubmit={handleSubmit} />

The pattern is always the same: user does something, a function runs. When you are debugging a button that does not work, find its onClick first. That is the entry point.

useEffect

useEffect runs code in response to something happening in the component lifecycle. It looks intimidating but the rule is simple.

useEffect(() => {
  loadAgents();
}, []);

The second argument — the array — controls when it runs:

[]              -> run once when the component first appears
[selectedQueue] -> run whenever selectedQueue changes
no array        -> run after every render (rarely what you want)

So the example above means: “when this component first appears on screen, call loadAgents().” That is the most common pattern — load data when a page opens.

useEffect(() => {
  loadAgents();
}, [selectedQueue]);

This one means: “whenever selectedQueue changes, reload the agents.” You will see this pattern constantly for filtering and search.


The Full Data Flow

Put all five concepts together and you get the pattern that covers most of what a React frontend does:

function Dashboard() {

  const [agents, setAgents] = useState([]);

  useEffect(() => {
    loadAgents();
  }, []);

  async function loadAgents() {
    const response = await fetch("/api/agents");
    const data = await response.json();
    setAgents(data);
  }

  return (
    <AgentTable agents={agents} />
  );
}

Read it as a sequence:

Dashboard appears on screen
        |
useEffect fires ([] means once)
        |
loadAgents() runs
        |
fetch("/api/agents") calls the backend
        |
response comes back as JSON
        |
setAgents(data) updates state
        |
React redraws Dashboard
        |
agents passed as prop to AgentTable
        |
AgentTable renders the list
        |
user sees agents

If you understand that flow, you can read a surprising amount of React code without knowing anything else.


How to Navigate an Unfamiliar Project

You do not need to understand the whole codebase. You need to find the code that matters for the thing you are looking at.

Start the project

npm install
npm run dev

Open the localhost URL. Now you have a live browser and a code editor side by side. Change something, save, watch the browser update. That feedback loop is how you learn what does what.

Find visible text in the browser

Suppose the browser shows a button labelled “Filter Agents” and you want to know what it does.

Do not hunt through folders. In VS Code, press Ctrl + Shift + F and search for the exact text:

Filter Agents

You will find something like:

<Button onClick={handleFilter}>
  Filter Agents
</Button>

You have found the component. Now follow the event.

Follow the event handler

Search for handleFilter:

const handleFilter = () => {
  loadAgents(selectedFilter);
};

Follow loadAgents:

async function loadAgents(filter) {
  const response = await fetch(`/api/agents?filter=${filter}`);
  setAgents(await response.json());
}

You are now at the API call. The full chain:

click
  |
handleFilter()
  |
loadAgents(filter)
  |
fetch("/api/agents?filter=...")
  |
backend
  |
setAgents(data)
  |
UI redraws

This is the debugging path for almost every frontend problem. Find the UI element, find the event, follow the function, find the state or API call.

The question that cuts through confusion

When you are lost in a component, ask:

Where does the data come from, what changes it, and where is it displayed?

Answer those three questions and the component makes sense.


Reading TypeScript in React

You will see TypeScript everywhere. You do not need to master it. A few patterns come up constantly.

Type annotations on props:

interface AgentTableProps {
  agents: Agent[];
  onSelect: (id: string) => void;
}

function AgentTable({ agents, onSelect }: AgentTableProps) {

Read agents: Agent[] as “agents is an array of Agent objects.” Read onSelect: (id: string) => void as “onSelect is a function that takes a string and returns nothing.”

The ! operator means “I know this is not null”:

document.getElementById("root")!

The ? operator means “this might not exist”:

user?.name   // only access name if user exists

These two come up constantly. They are TypeScript’s way of handling values that might be missing.


Environment Variables

The .env file holds configuration that changes between environments — local development, staging, production.

VITE_API_URL=http://localhost:3000
VITE_FEATURE_FLAG=true

In code, they are accessed as:

const apiUrl = import.meta.env.VITE_API_URL;

Note the VITE_ prefix — Vite only exposes variables with that prefix to the browser. Anything without it stays server-side only.

If the app behaves differently in production than locally, the .env file is usually the first place to check.


Cheat Sheet

Component
  function that returns UI
  <AgentTable /> = call that function here

Props
  data passed from parent to child
  <AgentTable agents={agents} />

State
  const [value, setValue] = useState(initial)
  setValue(x) -> changes value AND redraws UI

Events
  onClick / onChange / onSubmit
  -> find the handler, follow the function

useEffect
  [], run once on mount
  [dep], run when dep changes

Data flow
  useEffect -> fetch -> setAgents -> UI redraws

Debugging path
  1. find visible text (Ctrl+Shift+F)
  2. find the component
  3. find onClick / onChange
  4. follow the function
  5. find state change or API call

TypeScript shortcuts
  Type!   = not null
  Type?   = might be null
  x[]     = array of x

Vite commands
  npm run dev    = local dev server
  npm run build  = production output in dist/

Environment variables
  .env file, VITE_ prefix, import.meta.env.VITE_NAME

UI libraries (MUI, antd, Bootstrap)
  pre-built components: Button, Table, Dialog, etc.
  import { Button } from '@mui/material'
  props documented on the library's own website
  React = house, UI library = furniture, Vite = tools

The next thing worth learning after this is how components share state across the tree — that leads into context and state management libraries. But the patterns above will get you through most of what you need to read and debug in an existing project.


[Note: This post was written with the help of an AI assistant. The material itself came from conversations with an LLM — I asked the questions, it explained the concepts, and I used it again to shape those explanations into a post.]

Comments