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>
);
}
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;
}
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)
This is cleaner than writing props.items and props.heading everywhere.
2. useState — Tracking Which Item Is Selected
const [selectdIndex, setSelectedIndex] = useState(-1);
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.
-
-1is the initial value, meaning nothing is selected yet -
selectdIndexholds the current state -
setSelectedIndexis the function we call to update it
💡 Quick note: There's a typo here —
selectdIndexshould beselectedIndex. 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>
);
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>}
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>
))}
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"
}
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);
}}
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:
- A parent component passes in an
itemsarray and aheadingstring - The component renders the heading, an empty-state message (if needed), and the list
- When you click an item,
setSelectedIndexupdates the state - React re-renders, and the clicked item gets highlighted
What I'd Improve Next
- Fix the typo:
selectdIndex→selectedIndex - Use
<>...</>shorthand instead of<Fragment>(cleaner to read) - Emit the selected item to the parent via an
onSelectItemcallback 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)