All skills
asyrafhussin avatar

/typescript-react-patterns

@e60e907

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.

Use this Skill: https://skilld.dev/gh/asyrafhussin/agent-skills/typescript-react-patterns

This session only. Nothing lands on disk.

rulescomp-display-name.md

≈1.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Display Name Pattern

Impact: CRITICAL (improves debugging and DevTools component identification)

Setting displayName for better debugging and DevTools integration. Components without displayName show as "Anonymous" in React DevTools, making debugging difficult.

Incorrect

// ❌ Bad
// Anonymous arrow function - shows as "Anonymous" in DevTools
export const Button = ({ children }: { children: React.ReactNode }) => {
  return <button>{children}</button>;
};

// HOC without displayName - hard to identify wrapped component
function withLogger<P extends object>(Component: React.ComponentType<P>) {
  return (props: P) => {
    console.log('Rendering:', props);
    return <Component {...props} />;
  };
}

// Memo without displayName
const ExpensiveList = React.memo(({ items }: { items: string[] }) => {
  return (
    <ul>
      {items.map((item) => (
        <li key={item}>{item}</li>
      ))}
    </ul>
  );
});

// ForwardRef without displayName
const Input = React.forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>(
  (props, ref) => <input ref={ref} {...props} />
);

// Context without displayName
const ThemeContext = React.createContext<'light' | 'dark'>('light');

Problems:

  • Anonymous components appear as "Anonymous" in React DevTools
  • HOCs without displayName hide the wrapped component identity
  • Error stack traces show meaningless component names
  • React Profiler cannot label components for performance analysis

Correct

// ✅ Good
import React, { forwardRef, memo, createContext } from 'react';

// Named function export - automatically has displayName
export function Button({ children }: { children: React.ReactNode }): React.ReactElement {
  return <button>{children}</button>;
}

// Arrow function with explicit displayName
export const IconButton = ({ icon, label }: { icon: React.ReactNode; label: string }): React.ReactElement => {
  return (
    <button aria-label={label}>
      {icon}
    </button>
  );
};
IconButton.displayName = 'IconButton';

// HOC with proper displayName
function withLogger<P extends object>(
  WrappedComponent: React.ComponentType<P>
) {
  const displayName = WrappedComponent.displayName ?? WrappedComponent.name ?? 'Component';

  function WithLogger(props: P) {
    console.log(`Rendering ${displayName}:`, props);
    return <WrappedComponent {...props} />;
  }

  WithLogger.displayName = `withLogger(${displayName})`;

  return WithLogger;
}

const LoggedButton = withLogger(Button);

// Memo with displayName
interface ListProps {
  items: string[];
  onItemClick?: (item: string) => void;
}

const ExpensiveList = memo<ListProps>(({ items, onItemClick }) => {
  return (
    <ul>
      {items.map((item) => (
        <li key={item} onClick={() => onItemClick?.(item)}>
          {item}
        </li>
      ))}
    </ul>
  );
});
ExpensiveList.displayName = 'ExpensiveList';

// ForwardRef with displayName
interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
  size?: 'sm' | 'md' | 'lg';
  error?: string;
}

const Input = forwardRef<HTMLInputElement, InputProps>(
  ({ size = 'md', error, className, ...rest }, ref) => {
    return (
      <div className="input-wrapper">
        <input
          ref={ref}
          className={`input input-${size} ${error ? 'input-error' : ''} ${className ?? ''}`}
          aria-invalid={!!error}
          {...rest}
        />
        {error && <span className="error">{error}</span>}
      </div>
    );
  }
);
Input.displayName = 'Input';

// Context with displayName
interface ThemeContextValue {
  theme: 'light' | 'dark';
  toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextValue | null>(null);
ThemeContext.displayName = 'ThemeContext';

// Helper function for creating displayName
function getDisplayName<P>(Component: React.ComponentType<P>): string {
  return Component.displayName ?? Component.name ?? 'Component';
}

Benefits:

  • Components appear with readable names in React DevTools
  • Stack traces show meaningful component names
  • Wrapped components show their hierarchy (e.g., "withLogger(Button)")
  • React Profiler shows component names for performance analysis
  • Component names appear in test output and snapshots

Reference: React TypeScript Cheatsheet

Source: SKILL.md on GitHub

No alerts16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill contains educational reference material and code documentation for TypeScript React development patterns. No security risks or malicious vectors were detected.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    30 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at e60e907. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated 7 months ago
metadata
{
  "author": "agent-skills",
  "version": "2.0.0"
}

README badge

README badge for asyrafhussin/agent-skills/typescript-react-patterns