Like button
Free · MITReact Native · ExpoA like toggle with one moment of delight: the heart squashes and springs back filled, a ring expands and particles burst while the count rolls; it updates optimistically and rolls back if the request fails.
Installation
$ npx shadcn@latest add https://goodui.dev/native/r/like-button.jsonThen run npx expo install --fix so react-native-reanimated, react-native-svg match your Expo SDK. Files land in components/goodui-native/like-button.
Usage
import { LikeButton } from "@/components/goodui-native/like-button/like-button";
export function PostActions({ post }: { post: Post }) {
return (
<LikeButton
defaultLiked={post.likedByMe}
count={post.likes}
format={{ notation: "compact" }}
onLikedChange={(liked) => api.setLike(post.id, liked)}
/>
);
}When to use
- Liking or favoriting content in a feed, a gallery or a product card.
- Reactions with a visible count (soft variant), such as on comments or posts.
- Saving or starring with a custom icon and the same burst.
- Toggles without delight or count, such as mute or bookmark in a toolbar: use icon-button with selected.
- Settings switches.
- Rating with several levels: use a rating control.
Examples
Reaction pill, red heart
<LikeButton variant="soft" tone="danger" size="sm" count={12} defaultLiked={false} />Controlled, with a custom star
<LikeButton
liked={starred}
onLikedChange={async (next) => {
await api.star(repo, next);
setStarred(next);
}}
accessibilityLabel="Star"
icon={({ liked, color, size }) => <StarIcon filled={liked} color={color} size={size} />}
/>API reference
| Prop | Type | Default |
|---|---|---|
| liked Controlled state. Update it after the promise from `onLikedChange` resolves; the button shows the new state meanwhile. | boolean | — |
| defaultLiked Uncontrolled starting state. | boolean | false |
| onLikedChange Called with the new state. Return a promise to make the change optimistic: the button flips at once and flips back, with an error haptic and a shake, if the promise rejects. | (liked: boolean) => unknown | — |
| count The total as your server reports it, including the viewer's like at that moment. The button adds or removes the viewer's like on top of it until the next count arrives. | number | — |
| format Intl options for the count, for example `{ notation: "compact" }` for "1.2K". | Intl.NumberFormatOptions | — |
| locale BCP 47 locale for the count. Defaults to the GooduiLocaleProvider's language, else the device locale. | string | — |
| icon Draw your own icon (star, bookmark, thumbs up). Receives the state, color and size. Defaults to a heart. | (state: LikeButtonIconState) => ReactNode | — |
| tone Color of the liked state. `accent` follows the theme; `danger` is the classic red heart. | "accent" | "danger" | "accent" |
| variant `plain` is the bare icon for feeds; `soft` is a pill that fills when liked, for reactions. | "plain" | "soft" | "plain" |
| size Icon 18, 22 or 28 pt. | "sm" | "md" | "lg" | "md" |
| burst Play the particle burst and ring on like. Default `true`. | boolean | true |
| disabled Dims the control and ignores presses. | boolean | false |
| haptic Cue on toggle (`pop` by default). `false` turns it off. | HapticName | false | "pop" |
| accessibilityLabel Read by screen readers; the selected state says whether it is on. Same as `labels.like`. | string | — |
| labels Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide. | Partial<LikeButtonLabels> | — |
| style Style for the outer container. | StyleProp<ViewStyle> | — |
| testID Identifier for end-to-end tests. | string | — |
Motion and haptics
- Like: the icon drops to 0.55 and springs back on the bouncy spring while the filled icon crossfades in; a ring expands from 0.4 to 1.6 and fades, and eight particles in long and short rays fly out, slow down and fade over 600 ms.
- Unlike: the icon dips to 0.82 and returns on the snappy spring, no burst.
- The count rolls through rolling-number: up on like, down on unlike or rollback.
- Press scales the button to 0.94 from touch down; a rejected request shakes it.
- The animation plays whenever the shown state changes, including a `liked` prop set from elsewhere (for example a double tap on a photo).
- Reduced motion: no burst, ring, squash, press scale or shake; the icon crossfades and the count changes in place.
- Haptic: pop on toggle (configurable with the haptic prop)
- Haptic: error when the request rejects
- The burst is nine views driven by one shared value on the UI thread, mounted only when burst is on and motion is not reduced.
- React renders on toggle, on settle and when count changes.
Accessibility
- Role button with accessibilityLabel (default 'Like'); selected in accessibilityState says whether it is liked; busy while the request is pending.
- The formatted count is the accessibilityValue; the rolling digits are hidden.
- A failed request is announced: 'Couldn't update. Try again.'
- A vertical hitSlop brings the target to 44 pt.
- On the web it is a toggle (aria-pressed) named by visually hidden text that includes the count ('Like, 1,284'), so the name always matches what is on screen.
Notes for AI
- Return the promise from onLikedChange for optimistic updates with rollback. Do not keep your own pending flag.
- count is the server total including the viewer's like at the time it was fetched. Do not add or subtract the viewer's like yourself.
- Controlled: set liked after the promise resolves; the button shows the new state meanwhile.
- Default tone is accent. tone='danger' gives the classic red heart.
- A custom icon is a function of { liked, color, size }; draw a filled shape when liked is true.
Related
A number whose digits roll in the direction of change, with digits that open and close in place and currency signs that glide instead of jumping.
A layered icon-only button whose surface and icon sink on touch down and release on different springs; it runs async actions itself, carries a badge and shows a hint on long press.
A count, dot or status badge that springs in when it appears, pops when its value changes, caps at 99+ and shrinks away at zero; BadgeAnchor pins it to an icon or avatar.