Loading...
Loading...
TypeScript best practices for React development. Use when writing typed React components, hooks, events, refs, or generic components. Triggers on tasks involving TypeScript errors, type definitions, props typing, or type-safe React patterns.
npx skill4agent add asyrafhussin/agent-skills typescript-react-patterns| Priority | Category | Impact | Prefix |
|---|---|---|---|
| 1 | Component Typing | CRITICAL | |
| 2 | Hook Typing | CRITICAL | |
| 3 | Event Handling | HIGH | |
| 4 | Ref Typing | HIGH | |
| 5 | Generic Components | MEDIUM | |
| 6 | Context & State | MEDIUM | |
| 7 | Utility Types | LOW | |
comp-props-interfacecomp-children-typescomp-default-propscomp-forward-refcomp-compoundcomp-polymorphichook-usestatehook-userefhook-useeffecthook-usereducerhook-custom-returnhook-genericevent-handler-typesevent-syntheticevent-formevent-keyboardevent-mouseevent-customref-dom-elementsref-mutableref-callbackref-forwardref-imperative-handlegeneric-listgeneric-formgeneric-selectgeneric-tablegeneric-constraintsctx-createctx-providerctx-consumerctx-reducerctx-default-valueutil-react-typesutil-component-propsutil-pick-omitutil-discriminated-unionsutil-assertion-functions// Use interface for props (extendable)
interface ButtonProps {
variant: 'primary' | 'secondary' | 'danger'
size?: 'sm' | 'md' | 'lg'
isLoading?: boolean
children: React.ReactNode
onClick?: () => void
}
// Use type for unions
type ButtonVariant = 'primary' | 'secondary' | 'danger'
function Button({
variant,
size = 'md',
isLoading = false,
children,
onClick,
}: ButtonProps) {
return (
<button
className={`btn-${variant} btn-${size}`}
onClick={onClick}
disabled={isLoading}
>
{isLoading ? 'Loading...' : children}
</button>
)
}// ReactNode - most flexible (string, number, element, array, null)
interface CardProps {
children: React.ReactNode
}
// ReactElement - only JSX elements
interface WrapperProps {
children: React.ReactElement
}
// Render prop pattern
interface DataFetcherProps<T> {
children: (data: T) => React.ReactNode
}
// Specific element type
interface TabsProps {
children: React.ReactElement<TabProps> | React.ReactElement<TabProps>[]
}// Form events
function Form() {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
// handle submit
}
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value)
}
const handleSelect = (e: React.ChangeEvent<HTMLSelectElement>) => {
console.log(e.target.value)
}
return (
<form onSubmit={handleSubmit}>
<input onChange={handleChange} />
<select onChange={handleSelect}>
<option>A</option>
<option>B</option>
</select>
</form>
)
}// DOM element ref
function Input() {
const inputRef = useRef<HTMLInputElement>(null)
const focus = () => {
inputRef.current?.focus()
}
return <input ref={inputRef} />
}
// Mutable ref (no null, stores values)
function Timer() {
const intervalRef = useRef<number | undefined>(undefined)
useEffect(() => {
intervalRef.current = window.setInterval(() => {
// tick
}, 1000)
return () => {
clearInterval(intervalRef.current)
}
}, [])
}// Generic list component
interface ListProps<T> {
items: T[]
renderItem: (item: T, index: number) => React.ReactNode
keyExtractor: (item: T) => string | number
}
function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
return (
<ul>
{items.map((item, index) => (
<li key={keyExtractor(item)}>{renderItem(item, index)}</li>
))}
</ul>
)
}
// Usage - T is inferred
<List
items={users}
renderItem={(user) => <UserCard user={user} />}
keyExtractor={(user) => user.id}
/>interface AuthContextType {
user: User | null
login: (credentials: Credentials) => Promise<void>
logout: () => void
isLoading: boolean
}
const AuthContext = createContext<AuthContextType | null>(null)
export function useAuth() {
const context = useContext(AuthContext)
if (!context) {
throw new Error('useAuth must be used within AuthProvider')
}
return context
}
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [isLoading, setIsLoading] = useState(true)
const login = async (credentials: Credentials) => {
// implementation
}
const logout = () => {
setUser(null)
}
return (
<AuthContext.Provider value={{ user, login, logout, isLoading }}>
{children}
</AuthContext.Provider>
)
}rules/comp-props-interface.md
rules/hook-usestate.md
rules/event-handler-types.md