@rcaferati/react-awesome-button provides three related React button components:
AwesomeButton- animated base buttonAwesomeButtonProgress- progress flow wrapper on top ofAwesomeButtonAwesomeButtonSocial- social/share wrapper on top ofAwesomeButton
This README is updated for the current 8.2.0 release outputs, including base CSS, bundled theme CSS files, theme mapping modules, animated size transitions, and auto-width text choreography.
npm install @rcaferati/react-awesome-buttonImport the base stylesheet once, then render a button. This is the plain CSS path for buttons that do not pass a cssModule theme mapping.
import React from 'react';
import { AwesomeButton } from '@rcaferati/react-awesome-button';
import '@rcaferati/react-awesome-button/styles.css';
export default function Example() {
return (
<AwesomeButton type="primary" size="medium">
Button
</AwesomeButton>
);
}The React package uses two theme imports together:
- the theme CSS file, which contains the actual rules
- the theme mapping object, which is passed as
cssModule
import React from 'react';
import { AwesomeButton } from '@rcaferati/react-awesome-button';
import '@rcaferati/react-awesome-button/themes/theme-blue.css';
import themeBlue from '@rcaferati/react-awesome-button/themes/theme-blue';
export default function Example() {
return (
<AwesomeButton cssModule={themeBlue} type="primary" size="medium">
Button
</AwesomeButton>
);
}If you mix plain buttons and themed buttons, import both:
import '@rcaferati/react-awesome-button/styles.css';
import '@rcaferati/react-awesome-button/themes/theme-blue.css';theme-ambertheme-bluetheme-bojacktheme-brucetheme-c137theme-erictheme-flattheme-indigotheme-redtheme-rickiest
If href is not provided, AwesomeButton renders button-like behavior and calls onPress on successful activation.
import React from 'react';
import { AwesomeButton } from '@rcaferati/react-awesome-button';
import '@rcaferati/react-awesome-button/themes/theme-blue.css';
import themeBlue from '@rcaferati/react-awesome-button/themes/theme-blue';
export default function Example() {
return (
<AwesomeButton
cssModule={themeBlue}
type="primary"
size="medium"
onPress={() => {
// do something
}}>
Save changes
</AwesomeButton>
);
}If href is provided, the component renders anchor-like behavior and lets native navigation happen. Use containerProps for forwarded root element props such as target, rel, and aria-*.
import React from 'react';
import { AwesomeButton } from '@rcaferati/react-awesome-button';
import '@rcaferati/react-awesome-button/themes/theme-blue.css';
import themeBlue from '@rcaferati/react-awesome-button/themes/theme-blue';
export default function Example() {
return (
<AwesomeButton
cssModule={themeBlue}
type="link"
href="https://github.com/rcaferati"
containerProps={{ target: '_blank', rel: 'noreferrer noopener' }}>
Open website
</AwesomeButton>
);
}Use before and after to render icons. For icon-only buttons, pass the icon with before and omit children.
import React from 'react';
import { AwesomeButton } from '@rcaferati/react-awesome-button';
import '@rcaferati/react-awesome-button/themes/theme-blue.css';
import themeBlue from '@rcaferati/react-awesome-button/themes/theme-blue';
function StarIcon() {
return (
<svg aria-hidden="true" viewBox="0 0 24 24" width="16" height="16">
<path d="M12 3l1.9 4.8L19 9.2l-4 3.4 1.3 5.2L12 15l-4.3 2.8L9 12.6 5 9.2l5.1-1.4L12 3z" />
</svg>
);
}
function ArrowRightIcon() {
return (
<svg aria-hidden="true" viewBox="0 0 24 24" width="16" height="16">
<path d="M5 12h14" />
<path d="m13 5 7 7-7 7" />
</svg>
);
}
function PlayIcon() {
return (
<svg aria-hidden="true" viewBox="0 0 24 24" width="16" height="16">
<path d="M8 5v14l11-7z" />
</svg>
);
}
export default function Example() {
return (
<div style={{ display: 'grid', gap: 12 }}>
<AwesomeButton
cssModule={themeBlue}
type="primary"
size="medium"
before={<StarIcon />}
after={<ArrowRightIcon />}>
Continue
</AwesomeButton>
<AwesomeButton
cssModule={themeBlue}
type="primary"
size="medium"
before={<PlayIcon />}
containerProps={{ 'aria-label': 'Play' }}
/>
</div>
);
}size="small" | "medium" | "large"uses fixed button dimensionssize={null}uses content-driven auto width- fixed-size changes animate by default
- measured auto-width changes animate by default
animateSize={false}opts out of both fixed-size and measured auto-width animation- fixed-to-auto and auto-to-fixed transitions are intentionally instant in this release
Size animation timing is 125ms cubic-bezier(0.3, 0.05, 0.2, 1).
import React from 'react';
import { AwesomeButton } from '@rcaferati/react-awesome-button';
import '@rcaferati/react-awesome-button/styles.css';
export default function Example() {
return (
<div style={{ display: 'grid', gap: 12 }}>
<AwesomeButton type="primary" size="small">
Small
</AwesomeButton>
<AwesomeButton type="primary" size="medium">
Medium
</AwesomeButton>
<AwesomeButton type="primary" size="large">
Large
</AwesomeButton>
<AwesomeButton type="primary" size={null}>
Auto width grows with content
</AwesomeButton>
<AwesomeButton type="primary" size="large" animateSize={false}>
Instant fixed-size change
</AwesomeButton>
</div>
);
}Use textTransition when the button label is a plain string and you want label changes to animate.
When combined with size={null}, the current React behavior is:
- larger labels grow width first, then animate the text
- smaller labels animate the text first, then shrink the width
For the parity flow documented in the current stories, drive the label change from an external rerender rather than mutating the same button label from its own onPress.
import React from 'react';
import { AwesomeButton } from '@rcaferati/react-awesome-button';
import '@rcaferati/react-awesome-button/styles.css';
export default function Example() {
const [expanded, setExpanded] = React.useState(false);
const label = expanded ? 'Open analytics dashboard' : 'Open';
return (
<div style={{ display: 'grid', gap: 12 }}>
<AwesomeButton type="primary" size={null} textTransition>
{label}
</AwesomeButton>
<button type="button" onClick={() => setExpanded((value) => !value)}>
Toggle label length
</button>
</div>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
type |
string |
"primary" |
Visual variant key, such as primary, secondary, danger, link, or a social visual variant |
size |
"small" | "medium" | "large" | null |
null |
Size key or null for auto width |
active |
boolean |
false |
Controlled pressed/active visual state |
disabled |
boolean |
false |
Disables interactions |
visible |
boolean |
true |
Toggles visible state class |
placeholder |
boolean |
true |
If true and children is empty, renders placeholder/disabled state |
animateSize |
boolean |
true |
Animates fixed-size changes and measured auto-width changes |
textTransition |
boolean |
false |
Animates string-only label changes with a scrambling transition |
between |
boolean |
false |
Uses space-between layout for content |
ripple |
boolean |
false |
Enables ripple effect on successful activation |
moveEvents |
boolean |
true |
Enables pointer move position classes (left / middle / right) |
href |
string | null |
null |
Enables anchor-like mode |
element |
React.ElementType | null |
null |
Custom forwarded root element component |
rootElement |
string |
"aws-btn" |
Base class prefix |
className |
string | null |
null |
Extra class names |
style |
React.CSSProperties |
{} |
Inline styles for the root element |
containerProps |
object |
{} |
Additional props forwarded to the root element |
cssModule |
Record<string, string> | null |
null |
CSS module mapping object for themed usage |
before |
React.ReactNode |
null |
Content rendered before the label |
after |
React.ReactNode |
null |
Content rendered after the label |
extra |
React.ReactNode |
null |
Extra content rendered inside the wrapper |
children |
React.ReactNode |
null |
Main label/content |
onPress |
(event) => void |
null |
Called on successful activation/release |
onPressed |
(event) => void |
null |
Called when the press-in transition completes |
onReleased |
(element: HTMLElement) => void |
null |
Called when the release cycle clears |
onMouseDown |
(event) => void |
null |
Pointer/mouse/touch down callback |
onMouseUp |
(event) => void |
null |
Pointer/mouse/touch up callback |
AwesomeButtonProgress wraps AwesomeButton and manages a guarded progress lifecycle. Its onPress receives a second callback argument:
next(true)starts the success flownext(false, 'Failed')starts the error flow with an optional label override
import React from 'react';
import { AwesomeButtonProgress } from '@rcaferati/react-awesome-button';
import '@rcaferati/react-awesome-button/themes/theme-blue.css';
import themeBlue from '@rcaferati/react-awesome-button/themes/theme-blue';
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export default function Example() {
return (
<AwesomeButtonProgress
cssModule={themeBlue}
type="primary"
loadingLabel="Verifying..."
resultLabel="Verified!"
releaseDelay={500}
onPress={async (_event, next) => {
await sleep(900);
next(true);
}}>
Verify
</AwesomeButtonProgress>
);
}import React from 'react';
import { AwesomeButtonProgress } from '@rcaferati/react-awesome-button';
import '@rcaferati/react-awesome-button/styles.css';
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export default function Example() {
return (
<AwesomeButtonProgress
type="danger"
loadingLabel="Publishing..."
resultLabel="Done!"
onPress={async (_event, next) => {
await sleep(900);
next(false, 'Failed');
}}>
Publish
</AwesomeButtonProgress>
);
}Use showProgressBar={false} to keep the loading/result text flow while hiding the dark loading bar. Use progressLoadingTime to control how long the progress bar takes to advance during the loading phase.
import React from 'react';
import { AwesomeButtonProgress } from '@rcaferati/react-awesome-button';
import '@rcaferati/react-awesome-button/styles.css';
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export default function Example() {
return (
<AwesomeButtonProgress
type="primary"
loadingLabel="Syncing..."
resultLabel="Synced!"
showProgressBar={false}
progressLoadingTime={1500}
onPress={async (_event, next) => {
await sleep(900);
next(true);
}}>
Sync account
</AwesomeButtonProgress>
);
}import React from 'react';
import { AwesomeButtonProgress } from '@rcaferati/react-awesome-button';
import '@rcaferati/react-awesome-button/themes/theme-blue.css';
import themeBlue from '@rcaferati/react-awesome-button/themes/theme-blue';
function ShieldIcon() {
return (
<svg aria-hidden="true" viewBox="0 0 24 24" width="16" height="16">
<path d="M12 2l8 4v6c0 5-3.4 8.7-8 10-4.6-1.3-8-5-8-10V6l8-4z" />
</svg>
);
}
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export default function Example() {
return (
<AwesomeButtonProgress
cssModule={themeBlue}
type="primary"
size="medium"
before={<ShieldIcon />}
loadingLabel="Verifying..."
resultLabel="Verified!"
onPress={async (_event, next) => {
await sleep(900);
next(true);
}}>
Verify signature
</AwesomeButtonProgress>
);
}AwesomeButtonProgress accepts all AwesomeButton props except public active, and replaces the base onPress payload with the progress version below.
| Prop | Type | Default | Description |
|---|---|---|---|
onPress |
(event, next) => void |
null |
Progress handler. Call next(true) or next(false, label?) |
loadingLabel |
string |
"Wait.." |
Loading phase label |
resultLabel |
string |
"Success!" |
Success label |
releaseDelay |
number |
500 |
Delay before reset after progress end |
showProgressBar |
boolean |
true |
Hides the dark loading bar when false, while keeping the progress flow |
progressLoadingTime |
number |
6000 |
Loading-phase progress bar duration in milliseconds |
AwesomeButtonSocial wraps AwesomeButton and builds a share action when no custom onPress is provided.
On activation, the current React behavior is:
- if you provide
onPress, your handler is called and the built-in sharer logic is skipped - if
hrefis also present, native anchor navigation still happens becausehrefis forwarded to the base button - if there is no
href, mobile-capable environments attemptnavigator.share(...) - otherwise, the built-in sharer uses a type-specific web share URL, direct URL, or centered popup where supported
If you want a pure custom action with no native navigation, avoid combining onPress and href.
import React from 'react';
import { AwesomeButtonSocial } from '@rcaferati/react-awesome-button';
import '@rcaferati/react-awesome-button/themes/theme-blue.css';
import themeBlue from '@rcaferati/react-awesome-button/themes/theme-blue';
function LinkedinIcon() {
return (
<svg aria-hidden="true" viewBox="0 0 24 24" width="16" height="16">
<path d="M4 3.5A2.5 2.5 0 1 1 4 8.5a2.5 2.5 0 0 1 0-5zM2 10h4v11H2V10zm7 0h4v1.6c.6-1 1.9-1.9 3.9-1.9 4.2 0 5 2.8 5 6.4V21h-4v-4.4c0-2.1 0-3.8-2.3-3.8-2.3 0-2.6 1.8-2.6 3.7V21H9V10z" />
</svg>
);
}
export default function Example() {
return (
<AwesomeButtonSocial
cssModule={themeBlue}
type="linkedin"
before={<LinkedinIcon />}
sharer={{
url: 'https://example.com',
message: 'Check this out',
}}>
LinkedIn
</AwesomeButtonSocial>
);
}github is a visual style in the bundled themes. If you want a GitHub-looking button that opens a profile or repo, use href.
import React from 'react';
import { AwesomeButtonSocial } from '@rcaferati/react-awesome-button';
import '@rcaferati/react-awesome-button/themes/theme-blue.css';
import themeBlue from '@rcaferati/react-awesome-button/themes/theme-blue';
function GithubIcon() {
return (
<svg aria-hidden="true" viewBox="0 0 24 24" width="16" height="16">
<path d="M12 .5a12 12 0 0 0-3.8 23.4c.6.1.8-.3.8-.6v-2c-3.3.7-4-1.4-4-1.4-.6-1.4-1.3-1.8-1.3-1.8-1.1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1.1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.8-1.6-2.7-.3-5.5-1.3-5.5-5.9 0-1.3.5-2.4 1.2-3.2-.1-.3-.5-1.5.1-3.2 0 0 1-.3 3.3 1.2a11.5 11.5 0 0 1 6 0c2.3-1.5 3.3-1.2 3.3-1.2.7 1.7.2 2.9.1 3.2.8.8 1.2 1.9 1.2 3.2 0 4.6-2.8 5.6-5.5 5.9.4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0 0 12 .5z" />
</svg>
);
}
export default function Example() {
return (
<AwesomeButtonSocial
cssModule={themeBlue}
type="github"
href="https://github.com/rcaferati"
containerProps={{ target: '_blank', rel: 'noreferrer noopener' }}
before={<GithubIcon />}>
Open GitHub
</AwesomeButtonSocial>
);
}import React from 'react';
import { AwesomeButtonSocial } from '@rcaferati/react-awesome-button';
import '@rcaferati/react-awesome-button/styles.css';
export default function Example() {
return (
<AwesomeButtonSocial
type="whatsapp"
sharer={{
phone: '5511999999999',
message: 'Hello from AwesomeButton',
url: 'https://example.com',
}}>
WhatsApp
</AwesomeButtonSocial>
);
}instagram is treated as a direct URL fallback. It does not use a dedicated web popup sharer endpoint.
import React from 'react';
import { AwesomeButtonSocial } from '@rcaferati/react-awesome-button';
import '@rcaferati/react-awesome-button/styles.css';
export default function Example() {
return (
<AwesomeButtonSocial
type="instagram"
sharer={{
url: 'https://example.com',
message: 'Open this link',
}}>
Instagram
</AwesomeButtonSocial>
);
}If href is present, the component behaves like an anchor and does not execute the built-in share flow.
import React from 'react';
import { AwesomeButtonSocial } from '@rcaferati/react-awesome-button';
import '@rcaferati/react-awesome-button/styles.css';
export default function Example() {
return (
<AwesomeButtonSocial
type="github"
href="https://github.com/rcaferati"
containerProps={{ target: '_blank', rel: 'noreferrer noopener' }}>
Open GitHub
</AwesomeButtonSocial>
);
}AwesomeButtonSocial accepts all AwesomeButton props and adds the props below.
| Prop | Type | Default | Description |
|---|---|---|---|
sharer |
object |
{} |
Share payload source, with page metadata fallbacks where available |
dimensions |
{ width: number; height: number } |
{ width: 640, height: 480 } |
Popup window dimensions for share flows |
sharer.url |
string |
current page URL | URL to share, falling back to window.location.href |
sharer.message |
string |
page title | Share message/text, falling back to the document title |
sharer.image |
string |
og:image meta |
Image URL used by supported sharers such as Pinterest |
sharer.phone |
string |
null |
Phone number for WhatsApp |
sharer.user |
string |
null |
Username for Messenger direct flow |
The built-in sharer recognizes:
facebooktwitterpinterestlinkedinredditwhatsappmessengermailinstagram
You can still use other type values as visual styles if your theme includes them, but the built-in sharer URLs do not exist for unsupported types.
Use before and omit children:
<AwesomeButton before={<svg aria-hidden="true" viewBox="0 0 24 24" width="16" height="16"><path d="M8 5v14l11-7z" /></svg>} />For size={null} + textTransition, prefer an external rerender-driven label change:
const label = expanded ? 'Open analytics dashboard' : 'Open';
<AwesomeButton size={null} textTransition>{label}</AwesomeButton>Use:
onPress={async (_event, next) => {
// do work
next(true);
}}Do not try to control the progress state manually through active.
Use before for icons, href for visual-only social styles like github, and avoid combining onPress with href unless you explicitly want native navigation to continue.
import {
AwesomeButton,
AwesomeButtonProgress,
AwesomeButtonSocial,
} from '@rcaferati/react-awesome-button';import AwesomeButton from '@rcaferati/react-awesome-button/AwesomeButton';
import AwesomeButtonProgress from '@rcaferati/react-awesome-button/AwesomeButtonProgress';
import AwesomeButtonSocial from '@rcaferati/react-awesome-button/AwesomeButtonSocial';import '@rcaferati/react-awesome-button/styles.css';import '@rcaferati/react-awesome-button/themes/theme-blue.css';import themeBlue from '@rcaferati/react-awesome-button/themes/theme-blue';@rcaferati/react-awesome-button@rcaferati/react-awesome-button/AwesomeButton@rcaferati/react-awesome-button/AwesomeButtonProgress@rcaferati/react-awesome-button/AwesomeButtonSocial@rcaferati/react-awesome-button/styles.css@rcaferati/react-awesome-button/themes/theme-*.css@rcaferati/react-awesome-button/themes/theme-*@rcaferati/react-awesome-button/package.json
Rafael Caferati
Website: https://caferati.dev
LinkedIn: https://linkedin.com/in/rcaferati
Instagram: https://instagram.com/rcaferati
MIT
