Build reusable React buttons around a native <button>, a small set of purposeful props, and the native attributes your app already expects. Use buttons for actions and links for navigation; give icon-only controls an accessible name and keep keyboard focus visible.
Build a small native button component
React components let you combine interface elements into reusable, nestable components and configure them with props, as the React documentation explains. The prop names and visual variants below are design choices, not requirements imposed by React.
import type { ButtonHTMLAttributes } from 'react';
type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
variant?: 'primary' | 'secondary';
};
export function Button({
variant = 'primary',
type = 'button',
className = '',
...props
}: ButtonProps) {
return (
<button
type={type}
className={`button button--${variant} ${className}`.trim()}
{...props}
/>
);
}
This TypeScript example accepts normal button attributes, including children, disabled, onClick, and accessibility attributes, and passes them to the native element. The default type="button" prevents an ordinary control from accidentally submitting a surrounding form; callers can explicitly pass type="submit" when submission is intended.
Keep variants tied to meaningful design-system roles rather than exposing every CSS property as a prop. Add another variant only when it represents a real, repeated choice in the interface. For plain JavaScript, remove the type declarations and keep the same component behavior.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitches#1 Best Overall
Use a button for actions and a link for navigation
A button performs an action in the current interface, such as saving changes or opening a dialog. Navigation to another page or location belongs on a link. React Aria documents this distinction and provides a separate Link component; sharing visual styles does not require combining both semantics in one component (React Aria Button documentation).
A component that sometimes renders a button and sometimes an anchor has to preserve different native behavior and accessibility expectations. Prefer separate Button and Link components when both are needed. Carbon’s Button documentation likewise notes that substituting a non-button element brings accessibility considerations (Carbon Button documentation).
Keep native behavior, labels, and focus intact
- Use descriptive action text. Choose concise wording that makes the result clear, such as “Save changes,” rather than a vague label. The U.S. Web Design System recommends short, action-oriented button labels (USWDS Button guidance).
- Name icon-only controls. If the visible content is only an icon, provide an accessible name with
aria-labeloraria-labelledby; the graphic alone does not explain the action. - Do not discard the native element’s affordances. Preserve ordinary button attributes and event handlers by forwarding them to the underlying
<button>. Avoid recreating keyboard activation or focus behavior when the native element already supplies it. - Keep focus visible. Custom styles should retain a visible focus indicator, and the indicator and button text should be checked for contrast in the actual theme and context.
- Use the native disabled prop when disabling. A native
disabledbutton cannot be activated. By contrast,aria-disabled="true"communicates a state but does not itself prevent activation; application code must enforce that behavior, as USWDS notes.
For projects that want documented interaction and accessibility behavior while retaining control over DOM structure and styling, Adobe’s React Aria getting-started guide describes primitives that can be adopted incrementally. Its useButton documentation covers mouse, keyboard, touch, focus, and ARIA behavior, with the element type defaulting to a button. A native implementation keeps the dependency surface small but leaves those design and accessibility responsibilities with the application.
Add a pending state only when its behavior is defined
A spinner alone does not prevent repeated activation or announce that work is underway. If a control has a pending state, decide how it responds to activation, whether it remains focusable, and what assistive technology announces. React Aria’s Button documentation describes isPending as disabling press and hover while retaining focusability and announcing the pending state (React Aria Button documentation). A custom boolean prop does not automatically provide those behaviors; implement and verify them deliberately.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #3
Choose the implementation that fits the project
| Approach | What it offers | What your project owns |
|---|---|---|
| Native React component | Direct control with a minimal dependency surface; built on the browser’s button behavior. | The component API, styling, and any additional state or accessibility behavior. |
| React Aria behavior primitive | Documented interaction and accessibility behavior for mouse, keyboard, and touch, while leaving DOM structure and styling to the implementer. | Choosing its API and integrating the behavior into the project’s markup and styles. |
Use the native component when a small, application-specific wrapper is enough and the team can maintain its interaction details. Consider React Aria when its documented behavior is valuable and its API and dependency fit the design system. Neither route is universally best: the scope of the system and the behavior it needs should drive the choice.
Quick Recap
Best Value
Rank #4
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




