C.W.K.
Stream
Lesson 01 of 07 · published

React 19 + TS: Components and Props

~10 min · frameworks, react, react-19, components

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"A React component is a function that takes props and returns JSX. TypeScript makes that contract explicit."

Typing functional components

The modern convention is to declare a Props type and use it as the function parameter. function Button(props: ButtonProps): React.ReactNode { ... }. The return type is usually inferred as React.ReactNode (or JSX.Element in older React types) — annotating it explicitly is optional but helpful for documentation.

Don't use React.FC<Props> — it's the older pattern, implicitly adds children, and historically caused issues with default values. The direct Props approach is cleaner and what every modern style guide recommends.

Children typing

If your component accepts children, include them in Props: interface ButtonProps { children: React.ReactNode; onClick?: () => void }. React.ReactNode covers anything React can render — strings, numbers, elements, fragments, arrays, null. For specific types of children (e.g., 'only function children'), use a narrower type.

Event handler typing

React event handlers have specific types: onClick: (e: React.MouseEvent<HTMLButtonElement>) => void. The generic parameter is the element. Inline handlers usually let inference do the work — <button onClick={(e) => ...}> infers e correctly from the JSX context.

React + TS is mostly about typing Props and event handlers cleanly. The rest is just JavaScript with types.

Code

Component with Props, children, and event handlers·tsx
import { useState } from 'react';

interface ButtonProps {
  label: string;
  onClick: () => void;
  variant?: 'primary' | 'secondary';
  disabled?: boolean;
  children?: React.ReactNode;
}

export function Button({ label, onClick, variant = 'primary', disabled, children }: ButtonProps) {
  return (
    <button onClick={onClick} disabled={disabled} className={`btn btn-${variant}`}>
      {label}
      {children}
    </button>
  );
}

// Event handlers — inline inferred.
function Form() {
  const [value, setValue] = useState('');
  return (
    <input
      value={value}
      onChange={(e) => setValue(e.target.value)}    // e: React.ChangeEvent<HTMLInputElement>
    />
  );
}

External links

Exercise

Write a Card component with title: string, optional description?: string, and actions?: React.ReactNode for child action buttons. Use it in a parent component, passing actions as JSX children. Confirm the types work in both component definitions and the consumer.
Hint
interface CardProps { title: string; description?: string; actions?: React.ReactNode }. Then <Card title="..." actions={<Button ... />} />.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.