When designing shared components, it is essential to ensure that the native behavior of HTML is not compromised. For example, when using the button element, you should adhere to HTML standard attributes and types, such as type, disabled, aria-* attributes, and the onClick event handler.
Bad example
Implementations that pass only custom values to onClick event handlers significantly reduce maintainability and extensibility. Deviating from standard HTML attributes and event types can cause problems during future extensions.
// BAD: Button with missing HTML attributes or invalid type
type Props = {
label: string;
// Missing React.MouseEventHandler<HTMLButtonElement>
onClick?: (value: string) => void;
value?: string;
};
export const BadButton: React.FC<Props> = ({ label, onClick, className, value }) => {
return (
<button
// Missing HTML type/disabled/aria-*
className="inline-flex items-center rounded-md bg-blue-600 px-4 py-2 text-white"
onClick={() => onClick?.(value ?? label)}
>
<span>{label}</span>
</button>
);
};
Examples of improvements
HTML attributes are passed through as-is, and event handlers use standard types.
import React from "react";
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
variant?: "primary" | "secondary";
};
export const Button: React.FC<ButtonProps> = ({ className, variant = "primary", ...rest }) => {
const base = "inline-flex items-center justify-center rounded-md px-4 py-2 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50";
const style =
variant === "primary"
? "bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-600"
: "bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-400";
return (
<button className={[base, style, className].filter(Boolean).join(" ")} {...rest} />
);
};
<Button type="submit" value="hello" onClick={(e) => console.log(e.currentTarget.value)} />;
Top comments (0)