How to Read React Code as a Designer
React code looks intimidating at first. Here's a practical guide to decoding it using concepts you already know from Figma.

Reasons why it feels alien
When designers see React code for the first time, the instinct is to back away. Angle brackets, curly braces, unfamiliar keywords — it looks like a different language. The good news is that there are fewer patterns to learn than it seems.
Let's break it down.
The Basic Shape of a React Component
Here's the simplest React component you'll encounter:
export default function HelloWorld() {
return (
<div className='p-4 text-gray-900'>
<p>Hello, world</p>
</div>
);
}Reading it top to bottom:
export default function HelloWorld()— this is the component's name. Like a Figma component named "HelloWorld".return (...)— everything inside here is what gets rendered on screen. Think of it as your page/component/modal frame in the canvas.<div className="p-4 text-gray-900">— a container element. In Figma terms: a frame or rectangle with padding and a text colour.<p>Hello, world</p>— a paragraph of text inside that frame.
className vs class
If you've ever seen HTML, you might know that class is used to apply styles (colors, fonts, font size, padding, gap, border, corner radius, etc.). In React, it's className instead — a small quirk, but important to know so it doesn't trip you up.
Tailwind Classes Are Figma Properties
When you see className="p-4 text-gray-900 rounded-xl border", you're looking at design decisions written as utility classes:
| Tailwind class | Figma equivalent |
|---|---|
p-4 | Padding: 16 |
text-black | Text colour: black |
rounded-xl | Corner radius: 12 |
border | Stroke: 1px |
Once you know a handful of these mappings, AI-generated code becomes much more readable. You can scan a className and immediately visualise what the element looks like.
Nesting = Layers
The nesting in JSX (the HTML-like part of React) maps directly to Figma's layer hierarchy:
<div>
{' '}
{/* Frame */}
<div>
{' '}
{/* Frame */}
<p>Title</p> {/* Text */}
<p>Body</p> {/* Text */}
</div>
</div>Each indented level is a child inside a parent. Exactly like nesting frames in Figma.
Curly Braces Mean "JavaScript Here"
Whenever you see { and }, it means "stop treating this as markup/plain text and evaluate it as JavaScript":
<p>Hello, {name}</p>This renders "Hello, Amarneethi" if name variable is the string "Amarneethi". Think of it like a Figma text layer with a variable bound to it.
What to Do When You Don't Understand Something
Copy the confusing snippet and paste it into Claude or ChatGPT with the prompt: "Explain this React code to me as if I'm a designer who knows Figma but not code."
You'll get a plain-English explanation. Do this enough times, and the patterns start to feel familiar on their own.
DIY Exercise
Info
1. Component Composition
AI models generate self-contained components. Understanding how smaller pieces (a button, a card, an input) nest inside larger ones (a form, a dashboard, a page) lets you speak the same language as the generated code and request changes precisely — "move the avatar component inside the header" instead of "move that circle thing up."
Example 1 Code
function Avatar({ src, name, size = 'md' }) {
const sizes = {
sm: 'w-8 h-8 text-xs',
md: 'w-12 h-12 text-sm',
lg: 'w-16 h-16 text-base',
};
return (
<div
className={`${sizes[size]} rounded-full bg-gradient-to-br from-violet-500 to-fuchsia-500 flex items-center justify-center text-white font-bold shrink-0`}
>
{name.charAt(0)}
</div>
);
}
function UserCard({ name, role }) {
return (
<div className='flex items-center gap-3 p-3 rounded-xl bg-white/60 backdrop-blur border border-gray-200'>
<Avatar name={name} />
<div>
<p className='font-semibold text-gray-900'>{name}</p>
<p className='text-sm text-gray-500'>{role}</p>
</div>
</div>
);
}
function CompositionExample1() {
const team = [
{ name: 'Aarav Mehta', role: 'Design Lead' },
{ name: 'Priya Sharma', role: 'Engineer' },
{ name: 'Rohan Kapoor', role: 'PM' },
];
return (
<div className='space-y-3 p-6 bg-gray-50 rounded-2xl max-w-sm'>
<h3 className='font-bold text-gray-800 text-lg'>Design Team</h3>
{team.map((person) => (
<UserCard key={person.name} name={person.name} role={person.role} />
))}
</div>
);
}Output
Design Team
Aarav Mehta
Design Lead
Priya Sharma
Engineer
Rohan Kapoor
PM
Example 2 Code
function NavItem({ icon, label, active = false }) {
return (
<button
className={`flex items-center gap-3 w-full px-4 py-2.5 rounded-lg text-left transition-colors ${
active
? 'bg-indigo-100 text-indigo-700 font-semibold'
: 'text-gray-600 hover:bg-gray-100'
}`}
>
<span className='text-lg'>{icon}</span>
<span className='text-sm'>{label}</span>
</button>
);
}
function CompositionExample2() {
return (
<nav className='w-56 p-4 bg-white rounded-2xl border border-gray-200 space-y-1'>
<p className='text-xs font-bold text-gray-400 uppercase tracking-wider px-4'>
Menu
</p>
<NavItem icon={<Gauge size='12' />} label='Dashboard' active />
<NavItem icon={<ChartNoAxesCombined size='12' />} label='Analytics' />
<NavItem icon={<User2 size='12' />} label='Profile' />
<NavItem icon={<Settings size='12' />} label='Settings' />
</nav>
);
}Output
Example 3 Code
function Badge({ label, color = 'blue' }) {
const colors = {
blue: 'bg-blue-100 text-blue-700',
green: 'bg-emerald-100 text-emerald-700',
orange: 'bg-orange-100 text-orange-700',
};
return (
<span
className={`text-xs font-semibold px-2.5 py-1 rounded-full ${colors[color]}`}
>
{label}
</span>
);
}
function ProjectCard({ title, status, color }) {
return (
<div className='p-4 rounded-xl bg-white border border-gray-200 space-y-2'>
<Badge label={status} color={color} />
<h4 className='font-semibold text-gray-900'>{title}</h4>
<div className='w-full h-1.5 bg-gray-100 rounded-full overflow-hidden'>
<div
className='h-full bg-indigo-500 rounded-full'
style={{ width: '65%' }}
/>
</div>
</div>
);
}
function CompositionExample3() {
return (
<div className='grid grid-cols-1 sm:grid-cols-3 gap-4 max-w-2xl'>
<ProjectCard title='Brand Refresh' status='In Progress' color='blue' />
<ProjectCard title='Mobile App' status='Review' color='orange' />
<ProjectCard title='Design System' status='Done' color='green' />
</div>
);
}Output
Brand Refresh
Mobile App
Design System
2. Props as Design Tokens
Props are how React components accept variation — think of them as the knobs on a design system component. When AI generates <Button variant="primary" size="lg">, those props map directly to your design tokens. Learning this pattern means you can request exactly the variants you need and audit whether the output matches your design system.
Example 1 Code
function PropsExample1() {
const base =
'inline-flex items-center justify-center font-semibold rounded-xl transition-all active:scale-95';
const variants = {
primary: 'bg-gray-900 text-white hover:bg-gray-700',
secondary:
'bg-gray-100 text-gray-800 hover:bg-gray-200 border border-gray-200',
danger: 'bg-red-500 text-white hover:bg-red-600',
};
const sizes = {
sm: 'text-sm px-3.5 py-1.5',
md: 'text-sm px-5 py-2.5',
lg: 'text-base px-7 py-3',
};
return (
<div className='flex flex-wrap gap-3 items-center'>
{['primary', 'secondary', 'danger'].map((v) =>
['sm', 'md', 'lg'].map((s) => (
<button key={v + s} className={`${base} ${variants[v]} ${sizes[s]}`}>
{v} / {s}
</button>
)),
)}
</div>
);
}Output
Example 2 Code
function Alert({ intent = 'info', children }) {
const styles = {
info: {
bg: 'bg-sky-50 border-sky-200',
icon: <Info size={18} />,
text: 'text-sky-800',
},
success: {
bg: 'bg-emerald-50 border-emerald-200',
icon: <CheckCircle size={18} />,
text: 'text-emerald-800',
},
warning: {
bg: 'bg-amber-50 border-amber-200',
icon: <AlertCircle size={18} />,
text: 'text-amber-800',
},
error: {
bg: 'bg-red-50 border-red-200',
icon: <OctagonAlert size={18} />,
text: 'text-red-800',
},
};
const s = styles[intent];
return (
<div className={`flex items-center gap-3 p-4 rounded-xl border ${s.bg}`}>
<span className={`text-lg ${s.text}`}>{s.icon}</span>
<p className={`text-sm ${s.text}`}>{children}</p>
</div>
);
}
function PropsExample2() {
return (
<div className='space-y-3 max-w-md'>
<Alert intent='info'>Your trial expires in 3 days.</Alert>
<Alert intent='success'>Payment received successfully!</Alert>
<Alert intent='warning'>Storage is almost full.</Alert>
<Alert intent='error'>Failed to save changes.</Alert>
</div>
);
}Output
Your trial expires in 3 days.
Payment received successfully!
Storage is almost full.
Failed to save changes.
Example 3 Code
function Tag({ label, color = 'gray', rounded = false }) {
const colors = {
gray: 'bg-gray-100 text-gray-700',
purple: 'bg-purple-100 text-purple-700',
teal: 'bg-teal-100 text-teal-700',
pink: 'bg-pink-100 text-pink-700',
};
return (
<span
className={`inline-block text-xs font-medium px-3 py-1 ${rounded ? 'rounded-full' : 'rounded-md'} ${colors[color]}`}
>
{label}
</span>
);
}
function PropsExample3() {
return (
<div className='space-y-4'>
<div className='flex gap-2'>
<Tag label='Default' />
<Tag label='Purple' color='purple' />
<Tag label='Teal' color='teal' />
<Tag label='Pink' color='pink' />
</div>
<div className='flex gap-2'>
<Tag label='Rounded' rounded />
<Tag label='Purple Pill' color='purple' rounded />
<Tag label='Teal Pill' color='teal' rounded />
<Tag label='Pink Pill' color='pink' rounded />
</div>
</div>
);
}Output
3. Event-Driven UI
Everything interactive in React starts with an event — a click, a keystroke, a form submission, a scroll, a hover. AI-generated code wires these up with handlers like onClick, onChange, and onSubmit. As a designer, thinking in events rather than static screens is the fundamental shift: every interaction you design is really an event that triggers a response.
State is (must be) the result of events. When a user clicks a "like" button (event), the heart fills in because the state changed from false to true. When someone types in a search field (onChange event), the filtered list updates because the state now holds their query. Learning to trace this chain — event happens → state updates → UI re-renders — gives you a complete mental model of how AI-generated interfaces actually work. It also means you can prompt more precisely: "on click, toggle the isExpanded state and show the detail panel" instead of vaguely asking for "an expandable card."
Example 1 Code
function EventExample1() {
const [liked, setLiked] = useState(false);
const [count, setCount] = useState(42);
function handleLike() {
setLiked(!liked);
setCount((c) => (liked ? c - 1 : c + 1));
}
return (
<div className='flex items-center gap-6 p-6 bg-white rounded-2xl border border-gray-200 max-w-xs'>
<img
className='w-20 h-20 rounded-xl object-cover bg-gray-100'
src='https://picsum.photos/seed/design/200'
alt=''
/>
<div className='space-y-2'>
<p className='font-semibold text-gray-900'>Sunset Photo</p>
<button
onClick={handleLike}
className='flex items-center gap-1.5 text-sm transition-transform active:scale-90'
>
<span
className={`text-xl transition-colors ${liked ? 'text-red-500' : 'text-gray-300'}`}
>
{liked ? '♥' : '♡'}
</span>
<span className='text-gray-600'>{count}</span>
</button>
</div>
</div>
);
}Output
Sunset Photo
Example 2 Code
function EventExample2() {
const [query, setQuery] = useState("");
const fruits = ["Apple", "Banana", "Cherry", "Dragonfruit", "Elderberry", "Fig", "Guava", "Honeydew"];
const filtered = fruits.filter((f) => f.toLowerCase().includes(query.toLowerCase()));
return (
<div className="max-w-xs space-y-3">
<input
type="text"
placeholder="Search fruits…"
value={query}
onChange={(e) => setQuery(e.target.value)}
className="w-full px-4 py-2.5 rounded-xl border border-gray-200 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500/30 focus:border-indigo-400"
/>
<ul className="space-y-1">
{filtered.map((f) => (
<li key={f} className="px-4 py-2 rounded-lg bg-gray-50 text-sm text-gray-700">{f}</li>
))}
{filtered.length === 0 && (
<li className="px-4 py-2 text-sm text-gray-400 italic">No results</li>
)}
</ul>
</div>
);
}Output
- Apple
- Banana
- Cherry
- Dragonfruit
- Elderberry
- Fig
- Guava
- Honeydew
Example 3 Code
function EventExample3() {
const [items, setItems] = useState(["Learn React", "Ship v1"]);
const [value, setValue] = useState("");
function handleSubmit(e) {
e.preventDefault();
if (!value.trim()) return;
setItems([...items, value.trim()]);
setValue("");
}
return (
<div className="max-w-xs space-y-3">
<form onSubmit={handleSubmit} className="flex gap-2">
<input
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Add a task…"
className="flex-1 px-4 py-2.5 rounded-xl border border-gray-200 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500/30 focus:border-indigo-400"
/>
<button type="submit" className="px-4 py-2.5 bg-gray-900 text-white text-sm font-semibold rounded-xl hover:bg-gray-700 active:scale-95 transition-all">
Add
</button>
</form>
<ul className="space-y-1">
{items.map((item, i) => (
<li key={i} className="px-4 py-2.5 rounded-lg bg-gray-50 text-sm text-gray-700 flex items-center gap-2">
<span className="w-5 h-5 rounded-md border-2 border-gray-300 shrink-0" />
{item}
</li>
))}
</ul>
</div>
);
}Output
- Learn React
- Ship v1
4. Conditional Rendering
This is the code equivalent of "show this only when…" — a banner that appears on first visit, an error message on failed validation, a skeleton screen while data loads. AI code handles this with ternaries and logical operators. Knowing this pattern lets you spec interactions without ambiguity: "render the empty state when items.length === 0."
Example 1 Code
function ConditionalExample1() {
const [loggedIn, setLoggedIn] = useState(false);
return (
<div className='p-6 rounded-2xl bg-white border border-gray-200 max-w-xs space-y-4'>
{loggedIn ? (
<div className='space-y-3'>
<div className='flex items-center gap-3'>
<div className='w-10 h-10 rounded-full bg-gradient-to-br from-emerald-400 to-teal-500 flex items-center justify-center text-white font-bold'>
A
</div>
<div>
<p className='font-semibold text-gray-900 text-sm'>Aarav Mehta</p>
<p className='text-xs text-gray-500'>aarav@example.com</p>
</div>
</div>
<button
onClick={() => setLoggedIn(false)}
className='w-full py-2 text-sm text-red-600 bg-red-50 rounded-xl hover:bg-red-100 transition-colors'
>
Log Out
</button>
</div>
) : (
<div className='space-y-3 text-center'>
<p className='text-gray-500 text-sm'>You are not signed in.</p>
<button
onClick={() => setLoggedIn(true)}
className='w-full py-2.5 text-sm font-semibold text-white bg-gray-900 rounded-xl hover:bg-gray-700 active:scale-95 transition-all'
>
Sign In
</button>
</div>
)}
</div>
);
}Output
You are not signed in.
Example 2 Code
function ConditionalExample2() {
const [email, setEmail] = useState('');
const [touched, setTouched] = useState(false);
const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
const showError = touched && !isValid;
return (
<div className='max-w-xs space-y-2'>
<label className='text-sm font-medium text-gray-700'>Email address</label>
<input
type='email'
value={email}
onChange={(e) => setEmail(e.target.value)}
onBlur={() => setTouched(true)}
className={`w-full px-4 py-2.5 rounded-xl border text-sm focus:outline-none transition-colors ${
showError
? 'border-red-400 focus:ring-2 focus:ring-red-200'
: 'border-gray-200 focus:ring-2 focus:ring-indigo-500/30 focus:border-indigo-400'
}`}
placeholder='you@example.com'
/>
{showError && (
<p className='text-xs text-red-500 flex items-center gap-1'>
<span>⚠</span> Please enter a valid email address.
</p>
)}
{touched && isValid && (
<p className='text-xs text-emerald-600 flex items-center gap-1'>
<span>✓</span> Looks good!
</p>
)}
</div>
);
}Output
Example 3 Code
function ConditionalExample3() {
const [notifications, setNotifications] = useState([]);
function addRandom() {
const msgs = ["New comment on your post", "You were mentioned in #design", "Build succeeded", "Priya invited you"];
setNotifications((prev) => [...prev, msgs[prev.length % msgs.length]]);
}
return (
<div className="max-w-xs space-y-3">
<div className="flex items-center justify-between">
<h3 className="font-bold text-gray-800">Notifications</h3>
<button onClick={addRandom} className="text-xs text-indigo-600 font-semibold hover:underline"> + Simulate
</button>
</div>
{notifications.length === 0 ? (
<div className="py-10 text-center">
<p className="text-3xl mb-2">🔔</p>
<p className="text-sm text-gray-400">All caught up — nothing here!</p>
</div>
) : (
<ul className="space-y-2">
{notifications.map((msg, i) => (
<li key={i} className="flex items-start gap-3 p-3 rounded-xl bg-indigo-50 text-sm text-gray-700">
<span className="w-2 h-2 mt-1.5 rounded-full bg-indigo-500 shrink-0" />
{msg}
</li>
))}
</ul>
)}
</div>
);
}Output
Notifications
🔔
All caught up — nothing here!
5. The map() List Pattern
Anytime you design a repeating element — a card grid, a table row, a notification list — the AI will use .map() to loop over data and render each item. Understanding this pattern helps you think in terms of a single template item plus variable data, which is exactly how you should hand off list-based designs.
Example 1 Code
function MapExample1() {
const users = [
{ id: 1, name: "Aarav Mehta", email: "aarav@example.com" },
{ id: 2, name: "Priya Sharma", email: "priya@example.com" },
{ id: 3, name: "Rohan Kapoor", email: "rohan@example.com" },
{ id: 4, name: "Diya Nair", email: "diya@example.com" },
];
return (
<div className="divide-y divide-gray-100 rounded-2xl border border-gray-200 overflow-hidden max-w-sm">
{users.map((user) => (
<div key={user.id} className="flex items-center gap-3 px-4 py-3 bg-white hover:bg-gray-50 transition-colors">
<div className="w-9 h-9 rounded-full bg-gradient-to-br from-violet-400 to-fuchsia-400 flex items-center justify-center text-white text-sm font-bold">
{user.name.charAt(0)}
</div>
<div>
<p className="text-sm font-medium text-gray-900">{user.name}</p>
<p className="text-xs text-gray-400">{user.email}</p>
</div>
</div>
))}
</div>
);
}Output
Aarav Mehta
aarav@example.com
Priya Sharma
priya@example.com
Rohan Kapoor
rohan@example.com
Diya Nair
diya@example.com
Example 2 Code
function MapExample2() {
const plans = [
{ name: "Starter", price: 0, features: ["1 project", "Basic analytics", "Community support"] },
{ name: "Pro", price: 29, features: ["Unlimited projects", "Advanced analytics", "Priority support", "Custom domain"] },
{ name: "Enterprise", price: 99, features: ["Everything in Pro", "SSO & SAML", "Dedicated CSM", "SLA guarantee", "Audit logs"] },
];
return (
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 max-w-3xl">
{plans.map((plan) => (
<div key={plan.name} className="p-5 bg-white rounded-2xl border border-gray-200 flex flex-col">
<p className="text-sm font-bold text-gray-400 uppercase tracking-wide">{plan.name}</p>
<p className="mt-2 text-3xl font-extrabold text-gray-900">
${plan.price}<span className="text-base font-normal text-gray-400">/mo</span>
</p>
<ul className="mt-4 space-y-2 flex-1">
{plan.features.map((f) => (
<li key={f} className="flex items-center gap-2 text-sm text-gray-600">
<span className="text-emerald-500">✓</span> {f}
</li>
))}
</ul>
<button className="mt-5 w-full py-2.5 rounded-xl text-sm font-semibold bg-gray-900 text-white hover:bg-gray-700 active:scale-95 transition-all">
Choose {plan.name}
</button>
</div>
))}
</div>
);
}Output
Starter
$0/mo
- ✓ 1 project
- ✓ Basic analytics
- ✓ Community support
Pro
$29/mo
- ✓ Unlimited projects
- ✓ Advanced analytics
- ✓ Priority support
- ✓ Custom domain
Enterprise
$99/mo
- ✓ Everything in Pro
- ✓ SSO & SAML
- ✓ Dedicated CSM
- ✓ SLA guarantee
- ✓ Audit logs
Example 3 Code
function MapExample3() {
const stats = [
{ label: "Revenue", value: "₹12.4L", change: "+12%", up: true },
{ label: "Users", value: "8,842", change: "+4.3%", up: true },
{ label: "Bounce Rate", value: "32%", change: "-2.1%", up: false },
{ label: "Avg. Session", value: "4m 12s", change: "+8%", up: true },
];
return (
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4 max-w-2xl">
{stats.map((s) => (
<div key={s.label} className="p-4 bg-white rounded-2xl border border-gray-200">
<p className="text-xs text-gray-400 font-medium">{s.label}</p>
<p className="text-2xl font-bold text-gray-900 mt-1">{s.value}</p>
<p className={`text-xs font-semibold mt-1 ${s.up ? "text-emerald-600" : "text-red-500"}`}>
{s.change}
</p>
</div>
))}
</div>
);
}Output
Revenue
₹12.4L
+12%
Users
8,842
+4.3%
Bounce Rate
32%
-2.1%
Avg. Session
4m 12s
+8%
These 5 patterns cover roughly 80% of what you'll encounter in AI-generated React. Mastering them turns you from someone who simply shoots in the dark into someone who can read, direct, and refine with AI.
The Payoff
The moment you can read a React component and roughly understand its structure, you become a much more effective collaborator with AI. You can verify that what it generated matches your intent. You can ask for specific changes. You can spot when it's gone off-track.
You don't need to write React from scratch to benefit from understanding it. You just need to read it well enough to direct AI.