Skilllibrary react-typescript
install
source · Clone the upstream repo
git clone https://github.com/merceralex397-collab/skilllibrary
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/merceralex397-collab/skilllibrary "$T" && mkdir -p ~/.claude/skills && cp -r "$T/08-web-frontend-and-design/react-typescript" ~/.claude/skills/merceralex397-collab-skilllibrary-react-typescript && rm -rf "$T"
manifest:
08-web-frontend-and-design/react-typescript/SKILL.mdsource content
Purpose
Write type-safe React components using TypeScript: discriminated union props, generic components, forwardRef, and proper event typing.
When to use this skill
- typing component props with discriminated unions for variant patterns
- creating generic reusable components (
,<List<T>>
)<Select<T>> - forwarding refs with
and proper generic typingforwardRef - fixing TypeScript errors in React components or hooks
Do not use this skill when
- working with Next.js App Router specifics — prefer
nextjs-app-router - designing state architecture — prefer
state-management - building forms — prefer
forms-validation
Procedure
- Define props as types — use
(nottype
) for component props. Export for reuse.interface - Use discriminated unions — for variant props:
.type Props = { variant: 'primary'; icon: ReactNode } | { variant: 'ghost' } - Make generic components —
.function List<T>({ items, renderItem }: { items: T[]; renderItem: (item: T) => ReactNode }) - Forward refs correctly — use
. AddforwardRef<HTMLDivElement, Props>()
for DevTools.displayName - Type events —
. UseonClick: (e: React.MouseEvent<HTMLButtonElement>) => void
for inputs.React.ChangeEvent<HTMLInputElement> - Type hooks —
,useState<User | null>(null)
. AvoiduseRef<HTMLDivElement>(null)
.any - Use
— extend native element props:ComponentPropsWithoutRef
.type Props = ComponentPropsWithoutRef<'button'> & { variant: string } - Extract shared types — put reusable types in
. Co-locate component-specific types with the component.types.ts
Discriminated unions
type ButtonProps = | { variant: 'primary'; loading?: boolean; onClick: () => void } | { variant: 'link'; href: string } | { variant: 'icon'; icon: ReactNode; 'aria-label': string; onClick: () => void }; function Button(props: ButtonProps) { switch (props.variant) { case 'primary': return <button onClick={props.onClick} disabled={props.loading}>Submit</button>; case 'link': return <a href={props.href}>Link</a>; case 'icon': return <button onClick={props.onClick} aria-label={props['aria-label']}>{props.icon}</button>; } }
Generic components
type ListProps<T> = { items: T[]; renderItem: (item: T) => ReactNode; keyExtractor: (item: T) => string; }; function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) { return <ul>{items.map(item => <li key={keyExtractor(item)}>{renderItem(item)}</li>)}</ul>; } // Usage — T is inferred <List items={users} renderItem={u => <span>{u.name}</span>} keyExtractor={u => u.id} />
forwardRef pattern
type InputProps = ComponentPropsWithoutRef<'input'> & { label: string }; const Input = forwardRef<HTMLInputElement, InputProps>(({ label, ...props }, ref) => ( <div> <label>{label}</label> <input ref={ref} {...props} /> </div> )); Input.displayName = 'Input';
Decision rules
overtype
for props — unions requireinterface
; consistency matters.type- Never use
— it adds implicitReact.FC
and breaks generics.children - Discriminated unions over optional booleans —
notvariant: 'loading'
.isLoading?: boolean
to extend native props — avoids ref conflicts.ComponentPropsWithoutRef- Keep
explicit —children
not implicit.{ children: ReactNode }
References
Related skills
— Next.js-specific patternsnextjs-app-router
— typed state managementstate-management
— typed form patternsforms-validation