Button
A button whose visual state is a pure function of the timeline
A state atom whose appearance (idle, hover, press, loading, success) is a pure function of the state prop. Pass a state directly or drive it from the Remotion timeline via useCurrentState. The component reads no frame — only the state value you give it.
Installation
$ pnpm dlx shadcn@latest add @remocn/buttonInstalling button automatically installs spinner and the shared remocn-ui core (lib/remocn-ui/) via registryDependencies. You do not need to install either separately.
States
ButtonState is:
type ButtonState =
| "idle" // resting
| "hover" // lifted + background shift
| "press" // scale down + darken
| "loading" // renders <Spinner />
| "success" // renders a static checkmark, background fills primaryloading composes <Spinner /> in place of the label. success renders a static checkmark SVG. Both loading and success hide the label text.
Snap usage (simple path)
Pass state directly — the component snaps instantly to that visual. Useful for static previews or when you drive state from your own logic:
import { Button } from "@/components/remocn/button";
export const Scene = () => <Button state="loading" />;To drive state from the timeline, use useCurrentState:
import { useCurrentState } from "@/lib/remocn-ui";
import { Button } from "@/components/remocn/button";
export const Scene = () => {
const state = useCurrentState(
[
{ at: 8, state: "hover" },
{ at: 20, state: "press" },
{ at: 35, state: "loading" },
{ at: 70, state: "success" },
],
"idle",
);
return <Button label="Continue" state={state} />;
};Each at is a Sequence-local authored frame. State persists between steps: hover at frame 8 keeps the button lifted until press fires at frame 20. See Concepts for the full useCurrentState API.
Smooth transitions (opt-in)
State changes via state snap with no cross-fade. For animated transitions, use useButtonTransition from the copied use-button-transition.ts file. It reads the frame, interpolates between state presets, and returns a resolved ButtonStyle — pass it to the style prop:
import { Button } from "@/components/remocn/button";
import { useButtonTransition } from "@/components/remocn/use-button-transition";
export const Scene = () => {
const style = useButtonTransition([
{ at: 12, state: "hover" },
{ at: 30, state: "press" },
{ at: 48, state: "loading", duration: 6 },
{ at: 96, state: "success", duration: 16 },
]);
return <Button label="Continue" style={style} />;
};The duration field on each step overrides the file's DEFAULT_DURATION for that specific transition. To globally tune timing and easing, edit use-button-transition.ts directly in your project — that file is yours (shadcn "own your code" philosophy).
style takes precedence over state when both are provided.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
state | "idle" | "hover" | "press" | "loading" | "success" | "idle" | Current visual state (snap path). State changes snap — no automatic cross-fade. |
style | ButtonStyle | — | Resolved animated visual (smooth path). Pass an interpolated ButtonStyle from useButtonTransition. Takes precedence over state when provided. |
label | string | "Continue" | Button label text. Hidden during loading and success states. |
variant | "default" | "secondary" | "destructive" | "outline" | "ghost" | "default" | Visual variant. Matches shadcn button variant names. |
size | "sm" | "default" | "lg" | "default" | Button height and padding. sm=32px, default=40px, lg=48px. |
theme | Partial<RemocnTheme> | — | Per-component theme override. Merges with the active RemocnUIProvider theme and the mode defaults. Must be concrete oklch/hex/rgb values — not CSS custom properties. |
mode | "light" | "dark" | "light" | Sets the base palette. Overrides the mode on RemocnUIProvider when both are present. |
primary | string | — | Convenience override for the primary theme token. Merged into theme — saves a theme object for single-token changes. |
speed | number | 1 | Forwarded to the loading Spinner. Scales the spinner playhead — speed=2 spins twice as fast without changing authored step times. |
className | string | — | Optional className passed to the inner <button> element. |