react-syntax-components
react-syntax-components
Quick Reference
Function Component Typing
| Pattern | Syntax | When |
|---|---|---|
| Props interface + return | function Comp(props: Props): React.ReactElement |
Default for all components |
| Destructured props | function Comp({ name, age }: Props) |
When accessing props directly |
| Generic component | function List<T>(props: ListProps<T>) |
Reusable data-driven components |
| Default props | { size = 'md' }: Props |
Optional props with defaults |
Component API Quick Lookup
| API | Purpose | React 18 | React 19 |
|---|---|---|---|
React.memo |
Skip re-render when props unchanged | Yes | Yes (Compiler reduces need) |
forwardRef |
Pass ref through component | Required | DEPRECATED -- use ref as prop |
React.lazy |
Code-split with dynamic import | Yes | Yes |
createPortal |
Render outside DOM parent | Yes | Yes |
useImperativeHandle |
Expose custom ref handle | With forwardRef | With ref prop |
Critical Warnings
NEVER use class components for new code -- ALWAYS use function components with hooks. Class components are legacy and cannot use hooks.
NEVER define components inside other components -- this destroys state on every render. ALWAYS define components at module scope.
NEVER call hooks conditionally inside components -- React relies on consistent hook call order. ALWAYS call hooks at the top level.
ALWAYS use TypeScript interfaces for props -- bare any or untyped props defeat type safety and make refactoring dangerous.
ALWAYS use React.ReactNode for children type -- it covers strings, numbers, elements, arrays, fragments, portals, null, and undefined.
Decision Tree: Component Pattern Selection
Need to render children?
+-- Fixed structure --> Standard props interface
+-- Flexible layout --> children: React.ReactNode
+-- Parent needs control over rendering --> Render props pattern
Need to share behavior across components?
+-- Shared state logic --> Custom hook (ALWAYS prefer this)
+-- Shared rendering wrapper --> Compound component pattern
+-- Cross-cutting concern (rare) --> HOC pattern
Need performance optimization?
+-- Expensive render, stable props --> React.memo
+-- Code splitting by route --> React.lazy + Suspense
Need DOM access from parent?
+-- React 18 --> forwardRef + useImperativeHandle
+-- React 19 --> ref as prop + useImperativeHandle
Need to render outside DOM hierarchy?
+-- Modals, tooltips, overlays --> createPortal
Function Components with TypeScript
Props Interface Pattern
interface UserCardProps {
readonly name: string;
readonly email: string;
readonly role?: 'admin' | 'user' | 'guest'; // optional with union
readonly onSelect: (id: string) => void;
}
function UserCard({ name, email, role = 'user', onSelect }: UserCardProps): React.ReactElement {
return (
<div onClick={() => onSelect(name)}>
<h2>{name}</h2>
<p>{email}</p>
<span>{role}</span>
</div>
);
}
ALWAYS mark props as readonly -- props must never be mutated.
Generic Component Pattern
interface ListProps<T> {
readonly items: readonly T[];
readonly renderItem: (item: T, index: number) => React.ReactNode;
readonly keyExtractor: (item: T) => string;
}
function List<T>({ items, renderItem, keyExtractor }: ListProps<T>): React.ReactElement {
return (
<ul>
{items.map((item, index) => (
<li key={keyExtractor(item)}>{renderItem(item, index)}</li>
))}
</ul>
);
}
// Usage -- TypeScript infers T from items
<List items={users} renderItem={(user) => <span>{user.name}</span>} keyExtractor={(u) => u.id} />
Children Patterns
React.ReactNode (default)
interface CardProps {
readonly title: string;
readonly children: React.ReactNode;
}
function Card({ title, children }: CardProps): React.ReactElement {
return (
<div className="card">
<h3>{title}</h3>
<div className="card-body">{children}</div>
</div>
);
}
PropsWithChildren Utility
import { type PropsWithChildren } from 'react';
interface PanelProps {
readonly variant: 'primary' | 'secondary';
}
function Panel({ variant, children }: PropsWithChildren<PanelProps>): React.ReactElement {
return <div className={`panel-${variant}`}>{children}</div>;
}
Note: PropsWithChildren<P> adds children?: React.ReactNode -- children becomes optional.
React.memo -- Memoized Components
interface ExpensiveListProps {
readonly items: readonly string[];
readonly onItemClick: (item: string) => void;
}
const ExpensiveList = React.memo(function ExpensiveList(
{ items, onItemClick }: ExpensiveListProps
): React.ReactElement {
return (
<ul>
{items.map((item) => (
<li key={item} onClick={() => onItemClick(item)}>{item}</li>
))}
</ul>
);
});
Custom Comparison
const UserRow = React.memo(
function UserRow({ user, onSelect }: UserRowProps): React.ReactElement {
return <tr onClick={() => onSelect(user.id)}><td>{user.name}</td></tr>;
},
(prevProps, nextProps) => prevProps.user.id === nextProps.user.id
);
ALWAYS ensure parent stabilizes callback props with useCallback -- otherwise memo is ineffective because a new function reference is created every render.
React 19: The React Compiler auto-memoizes, making manual memo largely unnecessary. Keep memo for React 18 compatibility.
forwardRef and Ref Forwarding
React 19 -- ref as Regular Prop (PREFERRED)
interface InputProps {
readonly label: string;
readonly ref?: React.Ref<HTMLInputElement>;
}
function TextInput({ label, ref, ...props }: InputProps): React.ReactElement {
return (
<label>
{label}
<input ref={ref} {...props} />
</label>
);
}
React 18 -- forwardRef Required
interface InputProps {
readonly label: string;
}
const TextInput = React.forwardRef<HTMLInputElement, InputProps>(
function TextInput({ label, ...props }, ref): React.ReactElement {
return (
<label>
{label}
<input ref={ref} {...props} />
</label>
);
}
);
useImperativeHandle -- Custom Ref API
interface TextInputHandle {
focus: () => void;
clear: () => void;
}
// React 19
function TextInput({ ref }: { ref?: React.Ref<TextInputHandle> }): React.ReactElement {
const inputRef = React.useRef<HTMLInputElement>(null);
React.useImperativeHandle(ref, () => ({
focus() { inputRef.current?.focus(); },
clear() { if (inputRef.current) inputRef.current.value = ''; },
}), []);
return <input ref={inputRef} />;
}
// Parent usage
const inputRef = React.useRef<TextInputHandle>(null);
<TextInput ref={inputRef} />
// inputRef.current?.focus();
React 19 Ref Cleanup Function
// React 19 -- ref callbacks can return a cleanup function
<div ref={(node) => {
// Setup: node is attached
node?.addEventListener('scroll', handleScroll);
// Cleanup: returned function runs when ref detaches
return () => node?.removeEventListener('scroll', handleScroll);
}} />
React.lazy -- Code Splitting
// ALWAYS declare lazy components at module top level
const AdminPanel = React.lazy(() => import('./AdminPanel'));
const UserDashboard = React.lazy(() => import('./UserDashboard'));
function App(): React.ReactElement {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/admin" element={<AdminPanel />} />
<Route path="/dashboard" element={<UserDashboard />} />
</Routes>
</React.Suspense>
);
}
ALWAYS wrap lazy components in <Suspense> -- without it, a lazy component throws an error.
ALWAYS declare lazy() at module scope -- declaring inside a component recreates it every render and destroys state.
createPortal -- Rendering Outside the DOM Tree
import { createPortal } from 'react-dom';
interface ModalProps {
readonly isOpen: boolean;
readonly onClose: () => void;
readonly children: React.ReactNode;
}
function Modal({ isOpen, onClose, children }: ModalProps): React.ReactElement | null {
if (!isOpen) return null;
return createPortal(
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
{children}
</div>
</div>,
document.body
);
}
Event bubbling: Events from portals bubble through the React tree (not the DOM tree). A click inside a portal still triggers onClick on React ancestors.
ALWAYS use portals for modals, tooltips, and overlays that must escape overflow: hidden or z-index stacking contexts.
Composition Patterns
Compound Components (Context-based)
See references/patterns.md for the full compound component pattern with Context.
Render Props
See references/patterns.md for render props and function-as-children patterns.
Higher-Order Components (HOC)
See references/patterns.md for HOC typing with generics. ALWAYS prefer custom hooks over HOCs for new code.
Reference Links
- references/examples.md -- Complete component typing examples
- references/patterns.md -- Compound components, render props, HOC patterns
- references/anti-patterns.md -- Component mistakes and fixes
Official Sources
- https://react.dev/reference/react/memo
- https://react.dev/reference/react/forwardRef
- https://react.dev/reference/react/lazy
- https://react.dev/reference/react-dom/createPortal
- https://react.dev/reference/react/useImperativeHandle
- https://react.dev/learn/passing-props-to-a-component
- https://react.dev/learn/typescript