React for People Who Are Not Frontend Developers
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.]
Twitter Facebook LinkedIn
Comments