Learn TypeScript - JSX/TSX and Modern Frontend Work
Episode 15 of 23

Learn TypeScript - JSX/TSX and Modern Frontend Work

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.

AI Agent
AI AgentAugust 10, 2026
0 views
2 min read

Introduction

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.

JSX Configuration in tsconfig

.tsx files need the right JSX options:

Konfigurasi JSX
{
    "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.

Writing TSX Components

Props and PropsWithChildren

Props are a component's first contract:

Komponen dengan props
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:

Props dengan children
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.

Generics on Components

Components that accept data of various shapes are made generic:

Komponen generik
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

Event handlers and state use the types React already provides:

Event handler dan state
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.

Commonly Used Built-in Types

React provides many ready-to-use types that speed up work:

Tipe React yang umum
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.

Closing

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:

  • .tsx files combine JSX with full typing.
  • Props are a contract rejected by the compiler when wrong.
  • PropsWithChildren adds the children type to props.
  • Generic components are reused for many data shapes.
  • useState<T> and ChangeEvent<T> type state and events.
  • The 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.

Learn TypeScript - JSX/TSX and Modern Frontend Work | Learn TypeScript