This episode covers TypeScript in the modern frontend: JSX configuration in tsconfig, typing React components with props and children, event handlers, state with useState, and generics on components for safe reuse.

In the modern frontend, TypeScript and React are an almost inseparable pair. Files ending in .tsx combine JSX syntax with full typing. The result: wrong props are rejected by the compiler before the app ever runs.
Without types, props are just loose objects and event handlers accept anything. With types, every component becomes a clear contract: who uses the component, what types must be sent, and what users can do through handlers.
Episode 15 covers JSX setup in tsconfig, typing components and children, event handlers, state with generics, and generic components reused across many data shapes.
.tsx files need the right JSX options:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "react",
"module": "ESNext"
}
}The option jsx: "react-jsx" uses the automatic React 17+ transform, so you don't need to import React in every file. jsxImportSource points the compiler to the JSX runtime package. When using another framework like Preact, change the source value according to that framework's documentation.
Props are a component's first contract:
interface PropsTombol {
label: string;
aktif: boolean;
onClick: () => void;
}
export function Tombol({ label, aktif, onClick }: PropsTombol) {
return (
<button disabled={!aktif} onClick={onClick}>
{label}
</button>
);
}The interface PropsTombol defines what may be sent to the component. A consumer who forgets label or sends an onClick of the wrong shape immediately gets an error. Destructuring the parameter with the props type guarantees every value inside the component.
For components that accept children, use PropsWithChildren:
import type { PropsWithChildren } from "react";
interface PropsKartu extends PropsWithChildren {
judul: string;
}
export function Kartu({ judul, children }: PropsKartu) {
return (
<section>
<h2>{judul}</h2>
{children}
</section>
);
}PropsWithChildren adds a children property of type ReactNode to your props. The Kartu component can now wrap any element between its opening and closing tags.
Components that accept data of various shapes are made generic:
interface PropsList<T> {
items: T[];
render: (item: T) => React.ReactNode;
}
export function List<T>({ items, render }: PropsList<T>) {
return <ul>{items.map((item) => render(item))}</ul>;
}The declaration List<T> accepts a type parameter like a generic function. Used with items typed Pengguna[], the render function automatically receives Pengguna. Generic components combine reuse and type safety at once.
Event handlers and state use the types React already provides:
import { useState } from "react";
export function FormNama() {
const [nama, setNama] = useState<string>("");
function ubah(e: React.ChangeEvent<HTMLInputElement>) {
setNama(e.target.value);
}
return <input value={nama} onChange={ubah} />;
}useState<string> tells React that the state only holds strings, so setNama rejects anything else. The parameter e typed ChangeEvent<HTMLInputElement> gives safe access to e.target.value. React event types are imported from the React namespace.
React provides many ready-to-use types that speed up work:
import type { ReactNode, FC } from "react";
interface PropsSambutan {
nama: string;
extra?: ReactNode;
}
export const Sambutan: FC<PropsSambutan> = ({ nama, extra }) => {
return (
<div>
<p>Halo, {nama}</p>
{extra}
</div>
);
};ReactNode holds JSX elements, strings, numbers, or null. FC is a function component type that types the return value and props at once. These types keep component declarations short without losing clarity.
Info
Start prop names with on for callbacks, for example onSubmit, and use the type (e: FormEvent) => void instead of () => void when a handler needs the event. This convention makes component APIs easy to guess.
Episode 15 shows how TypeScript secures the frontend: props as contracts, typed event handlers, state whose shape is guaranteed, and generic components reused without losing type safety.
Key takeaways:
PropsWithChildren adds the children type to props.useState<T> and ChangeEvent<T> type state and events.ReactNode and FC types speed up component declarations.In the next episode 16 we'll discuss TypeScript in the Node.js backend — running TypeScript on the server, typing process.env, and handling async safely.