▶️ Live demo

Try it yourself — interact with the example below.

Loading demo…

🎯 What Are We Building?

Imagine you have a deck of question cards on a table. You flip one over to see the answer. When you flip a different card, the previous one flips back. If you flip the same card again, it closes.

THE FLASHCARD PATTERN:
┌─────────────────────────────────────────┐
│  📚 DECK OF 5 CARDS ON A TABLE          │
│                                         │
│  Card 1: "What is React?"               │
│  Card 2: "What is a Component?"         │
│  Card 3: "What is State?"               │
│  Card 4: "What is JSX?"                 │
│  Card 5: "What is Props?"               │
│                                         │
│  You can only see ONE answer at a time! │
│                                         │
│  Click Card 3 → Shows: "Memory of UI"   │
│  Click Card 1 → Card 3 closes!          │
│         Shows: "A JS library"           │
│  Click Card 1 again → Closes!           │
│                                         │
│  React needs to REMEMBER which card     │
│  is currently flipped!                  │
└─────────────────────────────────────────┘

⚠️ The Big Problem: "How Does React Remember Which Card Is Open?"

// ==========================================
// THE "EVERYTHING STUCK" TRAP
// ==========================================

// ❌ WRONG: No state = React forgets immediately!
function BadFlashcards() {
  // React has NO MEMORY of which card was clicked!
  
  return (
    <div>
      {questions.map(q => (
        <div key={q.id}>
          <p>{q.question}</p>
          {/* Where do we put the answer? Always show? */}
          <p>{q.answer}</p> {/* All answers show! ❌ */}
        </div>
      ))}
    </div>
  );
}

// Problems:
// 1. All answers show at once! (Messy!)
// 2. Clicking does nothing (no interactivity)
// 3. React doesn't know which card is "active"
// 4. No way to flip cards back and forth

// ==========================================
// THE SOLUTION: One State to Rule Them All
// ==========================================

// ✅ CORRECT: React remembers ONE selected ID

function GoodFlashcards() {
  // Step 1: Create state (null = nothing selected)
  const [selectedId, setSelectedId] = useState(null);

  return (
    <div className="flashcards">
      {questions.map(question => (
        <div 
          key={question.id}
          className={question.id === selectedId ? "selected" : ""}
          onClick={() => setSelectedId(question.id)}
        >
          {/* 
            Step 2: Use state to decide what to show
            If this card's ID matches selected ID → show answer
            Otherwise → show question
          */}
          <p>
            {question.id === selectedId 
              ? question.answer   // ← Flipped (show answer)
              : question.question // ← Normal (show question)
            }
          </p>
        </div>
      ))}
    </div>
  );
}

// What happens when user clicks Card 3 (id: 9103):
// 
// 1. User clicks Card 3
//    → onClick fires
//    → setSelectedId(9103)
//    → state updates to 9103
//    → React re-renders ALL cards
// 
// 2. React checks EACH card:
//    Card 1: id=1000, selectedId=9103 → NOT equal → shows question
//    Card 2: id=2000, selectedId=9103 → NOT equal → shows question
//    Card 3: id=9103, selectedId=9103 → EQUAL! → shows answer ✅
//    Card 4: id=4000, selectedId=9103 → NOT equal → shows question
//    Card 5: id=5000, selectedId=9103 → NOT equal → shows question
//
// 3. Only Card 3 shows its answer! Perfect!

📋 The 3-Step Technique (Flashcard Edition)

┌─────────────────────────────────────────┐
│  THE 3-STEP TECHNIQUE FOR FLASHCARDS:   │
│                                          │
│  Step 1: Create state (null = closed)   │
│    const [selectedId, setSelectedId] = useState(null);│
│                                          │
│  Step 2: Use state to decide content    │
│    {question.id === selectedId           │
│      ? question.answer                   │
│      : question.question}                │
│                                          │
│  Step 3: Update state on click          │
│    onClick={() => setSelectedId(question.id)}│
│                                          │
│  Result: React OWNS which card is open! │
└─────────────────────────────────────────┘

🧠 Complete Visual Breakdown

The Data Structure

// questions.js
const questions = [
  {
    id: 9103,
    question: "What is React?",
    answer: "A JavaScript library for building user interfaces"
  },
  {
    id: 2000,
    question: "What is a Component?",
    answer: "A reusable, independent piece of UI"
  },
  {
    id: 3000,
    question: "What is State?",
    answer: "Memory that makes components interactive"
  }
];

The State Box

┌─────────────────────────────────────────┐
│  REACT'S MEMORY BOX (useState)          │
│                                          │
│  Initial State:                          │
│  ┌─────────────────┐                   │
│  │  selectedId     │                   │
│  │  null           │  ← "Nothing open" │
│  └─────────────────┘                   │
│                                          │
│  After clicking Card 9103:             │
│  ┌─────────────────┐                   │
│  │  selectedId     │                   │
│  │  9103           │  ← "Card 3 open"  │
│  └─────────────────┘                   │
│                                          │
│  After clicking Card 2000:             │
│  ┌─────────────────┐                   │
│  │  selectedId     │                   │
│  │  2000           │  ← "Card 2 open"  │
│  │  (9103 forgot!) │                   │
│  └─────────────────┘                   │
│                                          │
│  After clicking Card 2000 again:        │
│  ┌─────────────────┐                   │
│  │  selectedId     │                   │
│  │  null           │  ← "Close it!"    │
│  └─────────────────┘                   │
└─────────────────────────────────────────┘

🔥 The Toggle Trick (The Hardest Part)

This is where most beginners get stuck. How do you close a card when you click it again?

// ==========================================
// THE TOGGLE LOGIC (Click Same = Close)
// ==========================================

// ❌ WRONG: Just sets the ID (can't close!)
onClick={() => setSelectedId(question.id)}

// If selectedId is 9103, and you click 9103 again:
// setSelectedId(9103) → state is already 9103!
// Nothing changes! Card stays open! ❌

// ✅ CORRECT: Toggle between ID and null
onClick={() => setSelectedId(
  question.id === selectedId ? null : question.id
)}

// If selectedId is 9103, and you click 9103 again:
// question.id (9103) === selectedId (9103) → TRUE
// → setSelectedId(null) → closes card! ✓

// If selectedId is 9103, and you click 2000:
// question.id (2000) === selectedId (9103) → FALSE
// → setSelectedId(2000) → opens card 2! ✓

Visual Flow:

┌─────────────────────────────────────────┐
│  TOGGLE LOGIC FLOW CHART                │
│                                          │
│  User clicks a card...                  │
│       ↓                                  │
│  Is this card already open?             │
│       ↓                                  │
│   YES ──→ Close it (set to null)        │
│       ↓                                  │
│    NO ──→ Open it (set to its ID)        │
│                                          │
│  Like a light switch!                   │
│  Click ON → Light is ON                 │
│  Click same switch → Light is OFF       │
└─────────────────────────────────────────┘

📦 Complete Code

Create file: Flashcards.jsx

import { useState } from "react";

// ==========================================
// THE DATA
// ==========================================
const questions = [
  {
    id: 9103,
    question: "What is React?",
    answer: "A JavaScript library for building user interfaces"
  },
  {
    id: 2000,
    question: "What is a Component?",
    answer: "A reusable, independent piece of UI"
  },
  {
    id: 3000,
    question: "What is State?",
    answer: "Memory that makes components interactive"
  },
  {
    id: 4000,
    question: "What is JSX?",
    answer: "Syntax that looks like HTML but is JavaScript"
  },
  {
    id: 5000,
    question: "What is Props?",
    answer: "Data passed from parent to child components"
  }
];

// ==========================================
// THE COMPONENT
// ==========================================
export default function Flashcards() {
  // Step 1: Create state
  // null = no card is selected (all show questions)
  const [selectedId, setSelectedId] = useState(null);

  return (
    <div className="flashcards">
      {/*
        Map over the array to create one card per question
        Each card needs a unique key (question.id)
      */}
      {questions.map((question) => (
        <div
          key={question.id}
          /*
            Step 2 (Part A): Conditional class
            If this card is selected, add "selected" class
            This changes the background color!
          */
          className={question.id === selectedId ? "selected" : ""}
          
          /*
            Step 3: Update state on click
            The TOGGLE logic:
            - If already open → close (null)
            - If closed → open (this card's ID)
          */
          onClick={() =>
            setSelectedId(
              question.id === selectedId ? null : question.id
            )
          }
        >
          {/*
            Step 2 (Part B): Conditional content
            If this card's ID matches selectedId → show ANSWER
            Otherwise → show QUESTION
          */}
          <p>
            {question.id === selectedId
              ? question.answer
              : question.question}
          </p>
        </div>
      ))}
    </div>
  );
}

🎨 CSS (styles.css)

.flashcards {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 20px;
  max-width: 800px;
  margin: 40px auto;
  font-family: Arial, sans-serif;
}

.flashcards div {
  background-color: #e7e7e7;
  border: 2px solid #ccc;
  border-radius: 10px;
  padding: 30px;
  height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  cursor: pointer;
  font-size: 18px;
  transition: all 0.3s;
}

/* When the "selected" class is added */
.flashcards div.selected {
  background-color: #e03131;
  color: white;
  border-color: #c92a2a;
  font-weight: bold;
}

.flashcards div:hover {
  transform: translateY(-3px);
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}

🧠 Memory Aids for Poor Logic Thinking

The "TV Remote" Analogy

┌─────────────────────────────────────────────────┐
│  FLASHCARDS = TV WITH ONE REMOTE CONTROL        │
│                                                 │
│  UNCONTROLLED (No state):                       │
│  ┌─────────────────────────────────────────┐    │
│  │  📺 5 TVs playing different channels   │    │
│  │                                          │    │
│  │  Each TV has its own physical button    │    │
│  │  You have no idea which one is on!      │    │
│  │  All TVs could be on at once!           │    │
│  │  Chaos! 😫                               │    │
│  └─────────────────────────────────────────┘    │
│                                                 │
│  CONTROLLED (One state):                        │
│  ┌─────────────────────────────────────────┐    │
│  │  📺 5 TVs, ONE REMOTE CONTROL          │    │
│  │                                          │    │
│  │  Remote has ONE number in memory:       │    │
│  │  "Currently watching channel 3"         │    │
│  │                                          │    │
│  │  Press 3 → TV 3 turns ON (red)          │    │
│  │  Press 1 → TV 3 turns OFF, TV 1 turns ON│    │
│  │  Press 1 again → TV 1 turns OFF         │    │
│  │                                          │    │
│  │  Only ONE TV can be on at a time!       │    │
│  │  Because remote only remembers ONE!     │    │
│  └─────────────────────────────────────────┘    │
│                                                 │
│  The Remote = selectedId state                  │
│  null = "No TV is on"                           │
│  9103 = "TV 3 is on"                            │
└─────────────────────────────────────────────────┘

The "School Locker" Analogy

┌─────────────────────────────────────────────────┐
│  FLASHCARDS = ROW OF SCHOOL LOCKERS             │
│                                                 │
│  Imagine 5 lockers in a hallway:                │
│                                                 │
│  [1]  [2]  [3]  [4]  [5]                        │
│   🔒   🔒   🔒   🔒   🔒                        │
│                                                 │
│  You have ONE master key that fits ONE locker.  │
│  The key has a number engraved on it:           │
│                                                 │
│  Key says: "Opens locker #3"                    │
│  → Locker 3 is open, all others locked!         │
│                                                 │
│  You walk to locker #2 and try the key:         │
│  → Key doesn't fit!                             │
│  → But a magic guard swaps the engraving!       │
│  → Key now says: "Opens locker #2"              │
│  → Locker 3 locks, Locker 2 opens!              │
│                                                 │
│  You walk back to locker #2:                    │
│  → Key fits!                                    │
│  → But you turn it the other way...             │
│  → Key now says: "Opens nothing" (null)        │
│  → Locker 2 locks!                              │
│                                                 │
│  In React:                                      │
│  • selectedId = the number on the key           │
│  • null = key has no number, all locked         │
│  • setSelectedId = the magic guard re-engraves  │
└─────────────────────────────────────────────────┘

The "Spotlight" Analogy

┌─────────────────────────────────────────────────┐
│  FLASHCARDS = STAGE WITH ONE SPOTLIGHT          │
│                                                 │
│  Imagine 5 actors on a stage:                   │
│                                                 │
│  🧑‍🎤  🧑‍🎤  🧑‍🎤  🧑‍🎤  🧑‍🎤                        │
│  Actor Actor Actor Actor Actor                  │
│   1     2     3     4     5                     │
│                                                 │
│  There is only ONE spotlight!                   │
│  The spotlight operator has a notebook:         │
│                                                 │
│  Notebook: "Shine on actor #3"                   │
│  → Actor 3 is lit up (red background)           │
│  → All others in darkness                       │
│                                                 │
│  Director shouts: "Actor 2!"                    │
│  → Operator writes: "Shine on actor #2"        │
│  → Spotlight moves! Actor 2 lit, 3 dark!       │
│                                                 │
│  Director shouts: "Actor 2 again!"             │
│  → Operator writes: "Shine on nobody" (null)   │
│  → Spotlight turns OFF! All dark!              │
│                                                 │
│  In React:                                      │
│  • selectedId = the notebook page number        │
│  • null = spotlight off                         │
│  • setSelectedId = director's command           │
│  • className = "selected" = the red spotlight  │
└─────────────────────────────────────────────────┘

The "If/Then Decision Tree" Analogy

┌─────────────────────────────────────────────────┐
│  THE DECISION TREE IN EVERY CARD                │
│                                                 │
│  When React renders EACH card, it asks:         │
│                                                 │
│            ┌─────────────┐                      │
│            │ Is my ID    │                      │
│            │ equal to    │                      │
│            │ selectedId? │                      │
│            └──────┬──────┘                      │
│                   │                             │
│         ┌────────┴────────┐                    │
│         │                 │                     │
│        YES              NO                      │
│         │                 │                     │
│         ▼                 ▼                     │
│   ┌──────────┐     ┌──────────┐                │
│   │ Show     │     │ Show     │                │
│   │ ANSWER   │     │ QUESTION │                │
│   │ (red)    │     │ (gray)   │                │
│   └──────────┘     └──────────┘                │
│                                                 │
│  This happens for EVERY card, EVERY render!    │
│                                                 │
│  Card 1: id=1000, selectedId=9103             │
│          1000 === 9103? NO → Question          │
│                                                 │
│  Card 3: id=9103, selectedId=9103             │
│          9103 === 9103? YES → Answer           │
└─────────────────────────────────────────────────┘

🎓 Practice Exercises

Exercise 1: Basic Flashcard List

Render a list of flashcards that shows questions (no click yet):

const data = [
  { id: 1, q: "What is HTML?", a: "HyperText Markup Language" },
  { id: 2, q: "What is CSS?", a: "Cascading Style Sheets" },
  { id: 3, q: "What is JS?", a: "JavaScript" }
];

function Flashcards() {
  // TODO: Map over data and show questions
  // TODO: Add keys!
  // TODO: Add a className "card" to each div

  return (
    <div className="flashcards">
      {/* Your code here */}
    </div>
  );
}

Solution:

function Flashcards() {
  return (
    <div className="flashcards">
      {data.map(item => (
        <div key={item.id} className="card">
          <p>{item.q}</p>
        </div>
      ))}
    </div>
  );
}

Exercise 2: Add the Toggle State

Add state so clicking a card shows its answer, and clicking again closes it:

function Flashcards() {
  // TODO: Create selectedId state (null)
  // TODO: Add onClick with toggle logic
  // TODO: Show answer if selected, question if not

  return (
    <div className="flashcards">
      {data.map(item => (
        <div key={item.id} className="card">
          {/* Show question or answer */}
        </div>
      ))}
    </div>
  );
}

Solution:

import { useState } from "react";

function Flashcards() {
  const [selectedId, setSelectedId] = useState(null);

  return (
    <div className="flashcards">
      {data.map(item => (
        <div
          key={item.id}
          className={item.id === selectedId ? "card selected" : "card"}
          onClick={() =>
            setSelectedId(item.id === selectedId ? null : item.id)
          }
        >
          <p>{item.id === selectedId ? item.a : item.q}</p>
        </div>
      ))}
    </div>
  );
}

Exercise 3: Fix the Broken Code

This code has 3 bugs. Find and fix them:

function Flashcards() {
  const [selectedId, setSelectedId] = useState(0); // Bug 1?

  return (
    <div className="flashcards">
      {questions.map(question => (
        <div
          className="selected" // Bug 2?
          onClick={setSelectedId(question.id)} // Bug 3?
        >
          <p>{question.question}</p>
        </div>
      ))}
    </div>
  );
}

Solution:

function Flashcards() {
  // Fix 1: Use null (not 0) for "nothing selected"
  const [selectedId, setSelectedId] = useState(null);

  return (
    <div className="flashcards">
      {questions.map(question => (
        <div
          key={question.id} // Also missing: key prop!
          
          // Fix 2: Conditional class, not always "selected"
          className={question.id === selectedId ? "selected" : ""}
          
          // Fix 3: Arrow function, not immediate call!
          onClick={() => setSelectedId(
            question.id === selectedId ? null : question.id
          )}
        >
          {/* Fix 4: Show answer when selected! */}
          <p>
            {question.id === selectedId
              ? question.answer
              : question.question}
          </p>
        </div>
      ))}
    </div>
  );
}

Exercise 4: Multiple Choice Quiz

Build a quiz where clicking an option shows if it's correct:

const questions = [
  {
    id: 1,
    question: "What is 2 + 2?",
    options: ["3", "4", "5"],
    correct: 1 // index of correct answer
  }
];

function Quiz() {
  // TODO: State to track which option was clicked
  // TODO: Show green if correct, red if wrong
  // TODO: Only evaluate after clicking

  return (
    <div>
      <h2>{questions[0].question}</h2>
      {/* Render options */}
    </div>
  );
}

Solution:

import { useState } from "react";

function Quiz() {
  const [selectedOption, setSelectedOption] = useState(null);
  const q = questions[0];

  return (
    <div style={{ padding: "20px", textAlign: "center" }}>
      <h2>{q.question}</h2>
      
      {q.options.map((opt, index) => (
        <button
          key={index}
          onClick={() => setSelectedOption(index)}
          style={{
            display: "block",
            margin: "10px auto",
            padding: "15px 30px",
            fontSize: "18px",
            border: "2px solid #ccc",
            borderRadius: "8px",
            cursor: "pointer",
            backgroundColor:
              selectedOption === null
                ? "white"
                : index === q.correct
                ? "#e8f5e9" // green
                : index === selectedOption
                ? "#ffebee" // red (wrong pick)
                : "white"
          }}
        >
          {opt}
          {selectedOption !== null && index === q.correct && " ✅"}
        </button>
      ))}
      
      {selectedOption !== null && (
        <p style={{ fontWeight: "bold" }}>
          {selectedOption === q.correct
            ? "Correct! 🎉"
            : "Wrong! Try again. ❌"}
        </p>
      )}
    </div>
  );
}

💡 Key Takeaways

ConceptWhat It MeansExample
Single state for many itemsOne variable controls which item is "active"const [selectedId, setSelectedId] = useState(null)
null as initial state"Nothing is selected"useState(null) not useState(0)
Conditional renderingShow different content based on stateid === selectedId ? answer : question
Conditional classChange styling based on stateclassName={id === selectedId ? "selected" : ""}
Toggle patternClick same = close, click different = switchsetSelectedId(id === selectedId ? null : id)
Arrow function in onClickPass function, don't call it!onClick={() => handle(id)}
key propReact needs unique IDs for listskey={question.id}
map()Transform array → JSX elementsarray.map(item => <div>...</div>)

The Flashcard Pattern:

function Flashcards() {
  // 1. State (null = nothing open)
  const [selectedId, setSelectedId] = useState(null);

  return (
    <div>
      {items.map(item => (
        <div
          key={item.id}
          // 2. Conditional class
          className={item.id === selectedId ? "selected" : ""}
          // 3. Toggle on click
          onClick={() => setSelectedId(
            item.id === selectedId ? null : item.id
          )}
        >
          {/* 2. Conditional content */}
          {item.id === selectedId ? item.answer : item.question}
        </div>
      ))}
    </div>
  );
}

Golden Rules:

  1. Use null for "nothing selected" — Not 0, not "", not undefined
  2. Always use key in lists — React needs it to track items
  3. Never call functions directly in JSXonClick={func} not onClick={func()}
  4. The toggle uses ternarycondition ? null : value is the magic
  5. One state controls many — Like a remote control for multiple TVs
  6. Conditional rendering is powerful? and : decide what appears
  7. React re-renders everything — When state changes, ALL cards check themselves

One Sentence Summary: > "The flashcard pattern uses a single piece of state called selectedId initialized to null to track which item is currently active, where you map over an array to render each item with a key prop and an onClick handler that uses a ternary toggle — setSelectedId(id === selectedId ? null : id) — to either close the current item or open a new one, while conditionally rendering different content and CSS classes by comparing each item's id to selectedId using the ternary operator, ensuring only one item can ever be active at a time!"