Convenciones de nomenclatura en React
Los event handlers arrancan con 'on' seguido del verbo en pasado del evento: onClick, onChange, onSubmit. Esto diferencia handlers (callbacks que tu componente recibe) de métodos internos (handleClick, handleChange). Un componente Button debería recibir onClick, no handleClick. El prefijo 'handle' es para métodos privados de tu componente.
Props booleanas usan prefijos is/has/should: isOpen, hasError, shouldRender. Evitá negativos como isNotDisabled; mejor: isEnabled. Los negativos dobles (disabled={!isNotHidden}) confunden. Una excepción: disabled es idiomático en HTML, así que disabled (no isDisabled) está bien para consistencia con el DOM.
Props de contenido que aceptan React.ReactNode deberían tener nombres descriptivos: leftIcon no icon, emptyState no fallback, errorMessage no error (error es mejor para el objeto Error). Esto hace el código autodocumentado: }> es más claro que }> cuando también existe rightIcon.
Patrones comunes de props
El patrón value/onChange es estándar para componentes controlados. Tu componente recibe value y llama onChange cuando el usuario interactúa. Incluí siempre defaultValue para la versión no controlada. Por ejemplo, Input puede funcionar como (controlado) o (no controlado).
Para componentes con múltiples estados de carga, usá el patrón status: 'idle' | 'loading' | 'success' | 'error'. Esto es mejor que tres booleanas (isLoading, isSuccess, isError) porque los estados son mutuamente excluyentes. Un componente no puede estar loading y success simultáneamente; el enum lo expresa claramente.
Cuando necesités flexibilidad de renderizado, el patrón render props es potente: renderItem={(item) => ...}. Esto permite al consumidor customizar la presentación sin necesidad de que tu componente exponga toda su estructura interna. DataTable con renderItem es más flexible que DataTable con itemClassName.
Tipos TypeScript para props
Usá genéricos para componentes reutilizables: List items={users}>, TypeScript sabe que renderItem recibe User.
Props opcionales necesitan el modificador ?. No uses undefined explícito (onClick: undefined | (() => void)) cuando podés escribir onClick?. Son equivalentes pero la sintaxis corta es idiomática. Para valores por defecto, usá destructuring: function Button({ size = 'md' }: Props) en lugar de size?: Size y luego const actualSize = size ?? 'md'.
Event handlers tipificados previenen errores: onChange?: (value: string) => void es mejor que onChange?: Function. Especificá el tipo del parámetro. Si tu componente pasa múltiples argumentos, hacelo explícito: onItemClick?: (item: T, index: number) => void. Esto documenta el contrato y TypeScript verifica que lo cumplás.
Composición vs configuración
Preferí composición cuando la customización es estructural. En lugar de
Usá props de configuración para variaciones predecibles. Un Button con variant='primary' | 'secondary' | 'ghost' es apropiado porque los estilos están definidos. No necesitás compound components para esto. Pero si el consumidor necesita estructura custom, composición gana: Tabs compound mejor que
El prop 'as' permite polimorfismo: