DEV Community

Daniel Keya
Daniel Keya

Posted on

Building My First React Component with TypeScript: A Beginner's Breakdown

So I just started learning React with TypeScript, and I want to share what I built and — more importantly — what I actually learned from it. If you're also new to this stack, this one's for you.

Here's the component I wrote:

import { useState } from "react";
import { Fragment } from "react/jsx-runtime";

interface Props {
  items: string[];
  heading: string;
}

export default function ListGroup({ items, heading }: Props) {
  const [selectdIndex, setSelectedIndex] = useState(-1);
  return (
    <Fragment>
      <h1>{heading}</h1>
      {items.length === 0 && <p>No item found</p>}
      <ul className="list-group">
        {items.map((item, index) => (
          <li
            className={
              selectdIndex === index
                ? "list-group-item active"
                : "list-group-item"
            }
            key={item}
            onClick={() => {
              setSelectedIndex(index);
            }}
          >
            {item}
          </li>
        ))}
      </ul>
    </Fragment>
  );
}
Enter fullscreen mode Exit fullscreen mode

It's a ListGroup component — a clickable list that highlights whichever item you select. Simple, but packed with fundamentals. Let me walk you through what I learned.


1. TypeScript Interfaces for Props

interface Props {
  items: string[];
  heading: string;
}
Enter fullscreen mode Exit fullscreen mode

In plain React (JavaScript), you can pass anything as props and React won't complain — until something breaks at runtime. TypeScript fixes this by letting you define exactly what a component expects.

Here, I'm saying: "This component must receive an array of strings (items) and a string (heading). Nothing else, nothing less."

The component signature then uses destructuring to pull those props out cleanly:

function ListGroup({ items, heading }: Props)
Enter fullscreen mode Exit fullscreen mode

This is cleaner than writing props.items and props.heading everywhere.


2. useState — Tracking Which Item Is Selected

const [selectdIndex, setSelectedIndex] = useState(-1);
Enter fullscreen mode Exit fullscreen mode

useState is React's way of giving a component memory. Without it, clicking an item would do nothing visible — React wouldn't know anything changed.

  • -1 is the initial value, meaning nothing is selected yet
  • selectdIndex holds the current state
  • setSelectedIndex is the function we call to update it

💡 Quick note: There's a typo here — selectdIndex should be selectedIndex. These things happen when you're learning! TypeScript won't catch variable name typos, only type mismatches.


3. Fragment — Because JSX Needs One Root Element

return (
  <Fragment>
    <h1>{heading}</h1>
    ...
  </Fragment>
);
Enter fullscreen mode Exit fullscreen mode

JSX has a rule: every component must return a single root element. But wrapping everything in an extra <div> just to satisfy that rule can mess up your CSS layout.

Fragment is the solution — it's an invisible wrapper that groups elements without adding anything to the DOM. You can also write it as the shorthand <>...</>.


4. Conditional Rendering

{items.length === 0 && <p>No item found</p>}
Enter fullscreen mode Exit fullscreen mode

This is a neat React pattern. If items.length === 0 is true, React renders the <p> tag. If it's false, React renders nothing. It's the JSX equivalent of an if statement, inline.


5. Rendering a List with .map()

{items.map((item, index) => (
  <li key={item}>
    {item}
  </li>
))}
Enter fullscreen mode Exit fullscreen mode

To render a list dynamically in React, you use .map() to transform each array item into a JSX element.

The key prop is important — React uses it internally to track which items have changed, been added, or removed. Without it, you'll get a console warning and potentially buggy re-renders.


6. Dynamic CSS Classes

className={
  selectdIndex === index
    ? "list-group-item active"
    : "list-group-item"
}
Enter fullscreen mode Exit fullscreen mode

This is a ternary operator used to conditionally apply a CSS class. If the current item's index matches selectdIndex, it gets the active class (which highlights it in Bootstrap). Otherwise, it just gets the base class.

It's a clean way to reflect state visually.


7. Handling Click Events

onClick={() => {
  setSelectedIndex(index);
}}
Enter fullscreen mode Exit fullscreen mode

When a list item is clicked, we call setSelectedIndex(index) with the clicked item's index. React then re-renders the component with the new state, and the correct item gets the active class.


Putting It All Together

Here's the full picture of what happens when you use this component:

  1. A parent component passes in an items array and a heading string
  2. The component renders the heading, an empty-state message (if needed), and the list
  3. When you click an item, setSelectedIndex updates the state
  4. React re-renders, and the clicked item gets highlighted

What I'd Improve Next

  • Fix the typo: selectdIndexselectedIndex
  • Use <>...</> shorthand instead of <Fragment> (cleaner to read)
  • Emit the selected item to the parent via an onSelectItem callback prop — right now the selection is trapped inside the component

Final Thoughts

This small component taught me a lot: TypeScript interfaces, state management, conditional rendering, list rendering, and event handling — all in ~30 lines of code. React with TypeScript felt intimidating at first, but once you see how the pieces fit together, it starts to click.

If you're also just starting out, I hope this breakdown helped. Let me know what you're building in the comments!


Tags: #react #typescript #beginners #webdev

Top comments (0)