HackIndia Logo
HackathonsNewsSkillsGrowth
HackIndia LogoContact: +91 9910125804

General

  • Companies
  • Colleges
  • Board of Advisors
  • Brand Ambassadors
  • FAQ
  • Contact
  • Report a Bug

Hackathons

  • Upcoming Hackathons
  • Judges
  • Mentors
  • Partners
  • Participating Colleges
  • Testimonials

Resources

  • About Us
  • Branding
  • Latest News
  • Growth
  • Skills
  • Builders
  • Ecosystem
  • Forum
  • Ideas
  • Prompt Optimizer
  • Games

© 2026 HackIndia. All rights reserved.

Privacy PolicyTerms of ServiceCode of ConductSitemap

Build 2.139 · 05 Nov 2026, 10:27 UTC

    HackIndia
    HackathonsNewsInternshipsSkillsGrowthPro Perks
    ← Build readable React apps

    All the reading

    React

    8 lessons, about 35 minutes. The check's questions come from these.

    Contents
    Starting React components
    1. 1. Start React the current way
    2. 2. Pass data with props
    Managing state and forms
    1. 3. Use state without clutter
    2. 4. Render lists with stable keys
    3. 5. Build controlled forms
    Sharing state and finishing apps
    1. 6. Lift state between components
    2. 7. Use effects only when needed
    3. 8. Ship your React repo

    Starting React components

    1. Start React the current way

    4 min

    In 30 seconds
    • Create a React 19 app with Vite instead of old Create React App tutorials.
    • Recognize React components as JavaScript functions that return JSX.
    • Make your first repo structure readable enough for later lessons.
    Key point

    One idea

    Start with a small Vite React app, then keep your UI as clear function components. A component is a JavaScript function that returns markup.

    Current starting point

    For this track, you will build a client-side React app with Vite. React docs show React 19.3 as current, but you do not need new advanced APIs for this project.

    Older tutorials may tell you to run Create React App. Do not use that for a new learning project. It is deprecated, and new React learning projects commonly use Vite, Parcel, or Rsbuild.

    Your to-do

    Create the app

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    npm create vite@latest my-app -- --template react-ts
    cd my-app
    npm install
    npm run dev

    What Vite gives you

    Vite gives you a fast local React setup. It starts as a client-only single page app. It does not decide routing, data fetching, or styling for you.

    That is fine for this track. You are proving React fundamentals first: components, props, state, forms, lists, lifted state, and useful effects.

    Example

    2. Pass data with props

    4 min

    In 30 seconds
    • Pass data from a parent component to a child using props.
    • Keep child components reusable by giving them only the data they need.
    • Avoid nesting component definitions inside other components.
    Key point

    One idea

    Props are data you pass from a parent component to a child component. They work like function arguments and let one component customize another component’s output.

    Why props matter

    Readable React apps are made from small components. A card, button, header, or project row can each be its own component.

    But small components need data. Instead of hardcoding the same text in many places, pass the changing parts as props from the parent.

    Example

    Project card example

    Imagine your HackIndia app shows student projects. The parent owns the project data. The child only knows how to display one project card.

    function ProjectCard({ title, builder, stack }) {
      return (
        <article>
          <h2>{title}</h2>
          <p>Built by {builder}</p>
          <p>Stack: {stack}</p>
        </article>
      );
    }
    
    export default function App() {
      return (
        <main>
          <h1>HackIndia Projects</h1>
          <ProjectCard
            title="Campus Canteen Queue"
            builder="Aarav"
            stack="React, Firebase"
          />
          <ProjectCard
            title="UPI Expense Splitter"
            builder="Meera"
            stack="React, Node.js"
          />
        </main>
      );
    }

    Managing state and forms

    3. Use state without clutter

    5 min

    In 30 seconds
    • Add useState only for data that changes after user action.
    • Keep computed values out of state so your component stays simple.
    • Update state with setters, not by mutating arrays or objects.
    Key point

    One idea

    State is component memory. Use it for data that changes over time because of user interaction, like a selected tab, a counter, or a saved draft title.

    Do not store data that comes from props or can be calculated during render. Less state means fewer bugs.

    What state is for

    In a React app, your component runs, returns JSX, and React shows it. When the user clicks, types, or selects something, the UI may need to remember a new value. That is when you reach for useState.

    For your HackIndia repo, reviewers should see state that has a reason. A filter button, a like count, an active project idea, or a form draft is clear. A duplicate value that can be calculated is clutter.

    Your to-do

    State checklist

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    Sharing state and finishing apps

    6. Lift state between components

    4 min

    In 30 seconds
    • Move shared state to the closest parent component.
    • Pass state down as props and pass handlers down for updates.
    • Keep one owner for each piece of changing data.
    Key point

    One owner

    When two components need the same changing data, do not copy it into both. Move that state to their closest common parent, then pass it down with props.

    The parent owns the data. Children display it or ask to change it by calling handler props.

    Why lift state

    Imagine a project idea filter. One component has the search box. Another component shows matching ideas. If the search text stays inside the search component, the list cannot use it.

    So the parent keeps searchText in state. The search box receives value and onChange. The list receives the filtered ideas.

    Your to-do

    Choose the parent

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    10 questions, 7 to pass.

    Sign in to take the test

    First readable component

    React component names must start with a capital letter. Lowercase JSX tags are treated as built-in HTML elements like div, button, and input.

    Multi-line JSX should be wrapped in parentheses after return. Keep the component at the top level, not inside another component.

    function App() {
      return (
        <main>
          <h1>Campus Project Tracker</h1>
          <p>Track ideas, teammates, and submission status.</p>
        </main>
      );
    }
    
    export default App;
    Common mistake

    Avoid nested components

    Do not define a child component function inside App just because it feels tidy. React docs warn this is slow and can cause bugs.

    If you need a child component later, define it at the top level in the same file while it is small. Split it into another file when the file gets crowded.

    // Avoid this pattern
    function App() {
      function Header() {
        return <h1>Campus Project Tracker</h1>;
      }
    
      return <Header />;
    }
    Tip

    Name the project clearly

    Use a project name a recruiter can understand in ten seconds. For example: campus-project-tracker, upi-expense-splitter, mess-menu-feedback, or internship-application-board.

    You can ask an AI tool for 10 app ideas, but check that the idea is small enough for a week and can show forms, lists, and state without needing paid APIs.

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Your to-do

    Read the flow

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Tip

    Destructure props

    For small components, destructure props in the parameter: function ProjectCard({ title, builder }). This keeps the JSX short and readable.

    If there are many props, pause. The component may be doing too much, or you may need to pass one object instead.

    Common mistake

    Do not nest components

    Do not define ProjectCard inside App just because App uses it. Component functions should be defined at the top level, not inside another component function.

    Nesting component definitions can make React recreate them in buggy ways. If a child needs data from a parent, pass props instead.

    // Avoid this pattern
    function App() {
      function ProjectCard({ title }) {
        return <h2>{title}</h2>;
      }
    
      return <ProjectCard title="Campus Canteen Queue" />;
    }

    Props are read-only

    A child component should use props to render. It should not try to change them.

    Later, when your app needs changing data, you will use state in the component that owns that data and pass the current value down through props.

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    0 of 5 done

    Example

    Counter example

    useState returns two things: the current value and a setter function. Call the setter in an event handler.

    Do not write count = count + 1. React will not treat that as a state update.

    import { useState } from 'react';
    
    export default function VoteButton() {
      const [votes, setVotes] = useState(0);
    
      function handleVote() {
        setVotes(votes + 1);
      }
    
      return (
        <button onClick={handleVote}>
          Votes: {votes}
        </button>
      );
    }
    Common mistake

    Avoid duplicate state

    A common beginner mistake is storing a computed value in state, then trying to keep it in sync. That adds extra code and can go stale.

    If you can calculate it from current data, calculate it while rendering. You do not need useEffect for ordinary calculation.

    function CartSummary({ items }) {
      const total = items.reduce((sum, item) => sum + item.price, 0);
    
      return <p>Total: ₹{total}</p>;
    }
    Example

    Update objects safely

    State should be replaced, not mutated. If your state is an object, create a new object with the changed field.

    This keeps React updates predictable and makes your code easier to inspect in a repo.

    import { useState } from 'react';
    
    export default function ProjectDraft() {
      const [draft, setDraft] = useState({
        title: 'UPI expense splitter',
        category: 'Fintech'
      });
    
      function renameProject() {
        setDraft({
          ...draft,
          title: 'Hostel expense splitter'
        });
      }
    
      return (
        <section>
          <h2>{draft.title}</h2>
          <p>{draft.category}</p>
          <button onClick={renameProject}>Rename</button>
        </section>
      );
    }
    Tip

    Use AI carefully

    You can ask an AI tool: “Review this React component. Which values should be state, and which should be computed during render?”

    Check the answer. If it suggests useEffect just to calculate a value from props or state, reject that pattern. Compute the value directly instead.

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    4. Render lists with stable keys

    5 min

    In 30 seconds
    • Render an array with map without making the JSX messy.
    • Choose keys from real data, not from the item’s position.
    • Show a clean project list in your Vite React repo.
    Key point

    One idea

    When you render a list, React needs a key for each item. Use a stable string or number from the data, like an id, so React can track items when the list changes.

    A key only needs to be unique among its siblings. It does not need to be globally unique across your whole app.

    Why keys matter

    React renders lists with normal JavaScript. Most of the time, you will use array.map() to turn data into JSX.

    If you add, remove, or reorder items, React uses keys to understand what changed. Without good keys, React may reuse the wrong row and show confusing UI, especially when each row later has inputs or local state.

    Example

    Project list example

    Keep the data as an array. Then map over it in JSX. Put the key on the outer element returned by map().

    This example fits an internship-style portfolio app. You can replace the sample projects with your own hackathon projects.

    const projects = [
      {
        id: "upi-splitter",
        name: "UPI Splitter",
        stack: "React, CSS",
        status: "Submitted"
      },
      {
        id: "mess-menu",
        name: "Mess Menu Tracker",
        stack: "React, Firebase",
        status: "In progress"
      },
      {
        id: "bus-pass",
        name: "Bus Pass Reminder",
        stack: "React, Local Storage",
        status: "Planned"
      }
    ];
    
    function ProjectList() {
      return (
        <section>
          <h2>Projects</h2>
    
          <ul>
            {projects.map((project) => (
              <li key={project.id}>
                <h3>{project.name}</h3>
                <p>{project.stack}</p>
                <p>Status: {project.status}</p>
              </li>
            ))}
          </ul>
        </section>
      );
    }
    
    export default ProjectList;
    Your to-do

    Build the list

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Common mistake

    Avoid index keys

    Do not use the array index as the key for a list that can change order, add items, or delete items. The index is the item’s position, not the item’s identity.

    It may look fine in a static demo. Later, when you add filtering, sorting, forms, or delete buttons, index keys can make React preserve the wrong item state.

    // Avoid this for changing lists
    {projects.map((project, index) => (
      <li key={index}>{project.name}</li>
    ))}
    
    // Prefer this
    {projects.map((project) => (
      <li key={project.id}>{project.name}</li>
    ))}
    Tip

    Ask AI carefully

    You can ask ChatGPT, Gemini, or Claude to turn your project data into a React list. But check the keys yourself.

    If the AI gives key={index}, ask it to add stable ids to the data and use key={project.id}. Also check that it did not define a component inside another component just to render the row.

    Example

    Split the row

    If each row has more than a few lines, make a child component. Pass the item by props. Keep both components at the top level.

    The key stays where you call map(), not inside the child component.

    function ProjectCard({ project }) {
      return (
        <li>
          <h3>{project.name}</h3>
          <p>{project.stack}</p>
          <p>Status: {project.status}</p>
        </li>
      );
    }
    
    function ProjectList() {
      return (
        <ul>
          {projects.map((project) => (
            <ProjectCard key={project.id} project={project} />
          ))}
        </ul>
      );
    }
    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    5. Build controlled forms

    4 min

    In 30 seconds
    • Make form inputs follow React state instead of the browser’s hidden state.
    • Handle text inputs and checkboxes with the right value from the event.
    • Add a clean form component to your Vite React app for your repo.
    Key point

    One source

    A controlled form input gets its current value from React state and updates that state on every change. Text inputs use a string value prop. Checkboxes use a boolean checked prop.

    This makes your form easy to read, validate, reset, and submit because React owns the important form data.

    Why control forms

    In plain HTML, the browser remembers what the user typed. In React, you usually want that data in state so the rest of your component can use it.

    For your project, this matters in forms like adding a task, registering a team member, filtering events, or entering UPI payment details for a demo checkout.

    Your to-do

    Build the pattern

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Example

    Task form example

    This form controls a text input and a checkbox. Notice the checkbox reads e.target.checked, not e.target.value.

    You can place this in a component file, then pass the new item to a parent later when you learn lifted state.

    import { useState } from 'react';
    
    export default function TaskForm() {
      const [title, setTitle] = useState('');
      const [isUrgent, setIsUrgent] = useState(false);
      const [message, setMessage] = useState('');
    
      function handleSubmit(e) {
        e.preventDefault();
    
        const cleanTitle = title.trim();
        if (!cleanTitle) {
          setMessage('Enter a task title.');
          return;
        }
    
        setMessage(
          isUrgent ? `Added urgent task: ${cleanTitle}` : `Added task: ${cleanTitle}`
        );
        setTitle('');
        setIsUrgent(false);
      }
    
      return (
        <form onSubmit={handleSubmit}>
          <label>
            Task title
            <input
              value={title}
              onChange={(e) => setTitle(e.target.value)}
              placeholder="Finish React project"
            />
          </label>
    
          <label>
            <input
              type="checkbox"
              checked={isUrgent}
              onChange={(e) => setIsUrgent(e.target.checked)}
            />
            Mark as urgent
          </label>
    
          <button type="submit">Add task</button>
          <p>{message}</p>
        </form>
      );
    }
    Common mistake

    Avoid mixed control

    Do not let an input switch between controlled and uncontrolled. If a text input is controlled, always give it a string, not null or undefined.

    This is why the example starts title as ''. The input always has a safe string value, even before the user types.

    Tip

    Use AI carefully

    You can ask an AI tool to draft a React form, but check the important parts yourself. The input should have value plus onChange. A checkbox should have checked plus onChange.

    Also check that submit uses preventDefault, state is replaced with setters, and the code does not add useEffect just to copy form values around.

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    0 of 5 done

    Example

    Before and after

    Here, SearchBox should not own the search text if IdeaList also needs it. App is the closest common parent, so App owns searchText.

    Notice the direction. Data goes down. Events go up through handler props.

    import { useState } from 'react';
    
    const ideas = [
      { id: 'upi-split', title: 'UPI bill splitter' },
      { id: 'mess-menu', title: 'Hostel mess menu tracker' },
      { id: 'notes-share', title: 'Class notes exchange' },
    ];
    
    export default function App() {
      const [searchText, setSearchText] = useState('');
    
      const visibleIdeas = ideas.filter((idea) =>
        idea.title.toLowerCase().includes(searchText.toLowerCase())
      );
    
      return (
        <main>
          <h1>Hackathon ideas</h1>
          <SearchBox value={searchText} onChange={setSearchText} />
          <IdeaList ideas={visibleIdeas} />
        </main>
      );
    }
    
    function SearchBox({ value, onChange }) {
      return (
        <label>
          Search ideas
          <input
            value={value}
            onChange={(event) => onChange(event.target.value)}
            placeholder="Try UPI"
          />
        </label>
      );
    }
    
    function IdeaList({ ideas }) {
      return (
        <ul>
          {ideas.map((idea) => (
            <li key={idea.id}>{idea.title}</li>
          ))}
        </ul>
      );
    }
    Common mistake

    Do not sync copies

    A common bug is keeping searchText in App and also localText in SearchBox, then trying to keep them equal later. That creates two sources of truth.

    For shared state, keep one source. If a value can be computed from current state or props, compute it during render. Do not store it as extra state.

    Tip

    Name handlers clearly

    Use prop names that show what the child can request. onChange is fine for an input wrapper. For app actions, use names like onAddIdea, onRemoveIdea, or onSelectIdea.

    This makes your repo easier to inspect. A recruiter can see which component owns state and which component only receives props.

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    7. Use effects only when needed

    4 min

    In 30 seconds
    • Decide when useEffect is actually needed in your React app.
    • Avoid effects for computed values, event work, and shared state fixes.
    • Add one justified effect, or write a clear note saying none is needed.
    Key point

    One rule

    Use useEffect only to synchronize your component with something outside React. Examples: browser storage, a timer, a map widget, a chat connection, or a non-React library.

    If you are only calculating a value from props or state, do it during render. If you are responding to a click or form submit, do it in the event handler.

    What counts external

    React already handles rendering from state and props. Your app does not need an effect to keep two React values in sync. That usually creates duplicate state and bugs.

    An external system is something React does not control. localStorage is outside React. document.title is outside React. A server connection is outside React.

    Common mistake

    Common wrong effect

    Beginners often store a filtered list in state, then use an effect to update it when the search text changes. That is not needed.

    The filtered list can be computed from the current list and search text during render. Less state means fewer bugs.

    function ProjectList({ projects, search }) {
      const visibleProjects = projects.filter((project) =>
        project.name.toLowerCase().includes(search.toLowerCase())
      );
    
      return (
        <ul>
          {visibleProjects.map((project) => (
            <li key={project.id}>{project.name}</li>
          ))}
        </ul>
      );
    }
    Example

    Useful effect

    This effect is justified because document.title belongs to the browser, not React. The dependency list includes the value used inside the effect.

    Call Hooks at the top level of your component. Do not put useEffect inside an if block, loop, or nested function.

    import { useEffect, useState } from 'react';
    
    export default function App() {
      const [projects, setProjects] = useState([]);
      const [name, setName] = useState('');
    
      useEffect(() => {
        document.title = `${projects.length} projects saved`;
      }, [projects.length]);
    
      function addProject(event) {
        event.preventDefault();
        const trimmedName = name.trim();
        if (!trimmedName) return;
    
        setProjects([
          ...projects,
          { id: crypto.randomUUID(), name: trimmedName }
        ]);
        setName('');
      }
    
      return (
        <form onSubmit={addProject}>
          <input
            value={name}
            onChange={(event) => setName(event.target.value)}
            placeholder="Project name"
          />
          <button>Add project</button>
        </form>
      );
    }
    Your to-do

    Effect checklist

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Tip

    Use AI carefully

    You can ask an AI tool to review your component: “Find useEffect calls that can be removed. Explain why.” Do not accept the answer blindly.

    Check three things yourself: is there an external system, are dependencies complete, and can the value be computed during render instead.

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    8. Ship your React repo

    5 min

    In 30 seconds
    • Prepare your Vite React repo so a reviewer can run it fast.
    • Check your app for readable components, props, state, forms, lists, and effects.
    • Write a clear README that proves your React choices.
    Key point

    Ship proof, not screenshots

    Your repo is the proof. A recruiter or HackIndia reviewer should be able to open it, install it, run it, and see the React skills from this track in the code.

    Do not hide everything in one large App file. Make the code easy to inspect.

    Your to-do

    Final repo checklist

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 6 done

    Example

    README starter

    Your README does not need to be long. It should answer three questions: what the app does, how to run it, and where the required React skills appear.

    Use this as a base and edit it for your project.

    # Campus Expense Splitter
    
    A small React app built with Vite. It helps students track shared canteen and travel expenses for a group.
    
    ## Run locally
    
    npm install
    npm run dev
    
    ## React skills shown
    
    - Components: App, ExpenseForm, ExpenseList, ExpenseItem, Summary
    - Props: expenses and handler functions are passed to child components
    - State: expenses, form fields, and selectedFilter use useState
    - Controlled form: ExpenseForm uses value and onChange for inputs
    - Lists: ExpenseList renders expenses with expense.id as the key
    - Lifted state: App owns expenses because the form, list, and summary all need it
    - Effect decision: No useEffect is needed because the app does not sync with an external system
    Common mistake

    Avoid fake effects

    Do not add useEffect just to look advanced. Effects are for syncing with something outside React, like browser storage, a timer, a map widget, or an API connection.

    If your totals, filters, or labels can be calculated from current props or state, calculate them during render. Write that in your README if you use no effect.

    Tip

    Use AI carefully

    You can ask ChatGPT, Gemini, or Claude to review your repo before submission. Paste your component tree or README and ask it to check against the rubric.

    Do not accept a rewrite blindly. Check that it keeps stable keys, controlled inputs, top-level component definitions, and no unnecessary effects.

    Prompt:
    Review this React Vite project for internship readability.
    Check for: readable components, props, useState, controlled forms, stable list keys, lifted state, and justified useEffect.
    Do not rewrite the whole app. Give me a checklist of issues to fix.
    Your to-do

    Before you submit

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done

    Your to-do

    Do this now

    Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.

    0 of 5 done