←
Swipe to Grow / Slide Button

Swipe to Grow / Slide Button

Capsule Slider

Interactive metallic capsule slider button with draggable glowing electric blue knob, illuminated neon channel fill, and dynamic masked text reveal.

Interactive Playground & Studio

Live Controls
slide-grow-button.tsx
Lets Grow!
Get Started
Hover or click to interact
Customizer
PRESETS & VARIATIONS

All Variants & Themes

Click to test interactive spring physics on every theme

Silver Steel Tracktheme="silver"
Lets Grow!
Get Started

Brushed aluminium capsule track with glowing electric blue slider knob.

Dark Metal Editiontheme="dark"
Lets Grow!
Get Started

Dark titanium chassis with bright neon blue photon channel fill.

UI Pirate Magmatheme="uipirate"
Lets Grow!
Get Started

Signature molten orange glow track with high-energy sliding physics.

Emerald Matrixtheme="emerald"
Lets Grow!
Get Started

Vibrant emerald photon illumination channel with smooth slider snap.

Features & Physics

✓Gesture-driven draggable capsule knob physics
✓Dynamic neon channel fill path as knob travels
✓Masked text reveal animation on swipe
✓Auto-snap completion threshold

Code & Integration

Customized live with your active playground prop settings:

"use client";

import React, { useState } from "react";
import { motion } from "framer-motion";

export type SlideGrowTheme = "silver" | "uipirate" | "dark" | "cyberpunk" | "emerald";
export type SlideGrowSize = "xs" | "sm" | "md" | "lg" | "xl";

export interface SlideGrowButtonProps {
  startLabel?: string;
  activeLabel?: string;
  theme?: SlideGrowTheme;
  size?: SlideGrowSize;
  onComplete?: () => void;
  className?: string;
}

export function SlideGrowButton({
  startLabel = "Get Started",
  activeLabel = "Lets Grow!",
  theme = "silver",
  size = "md",
  onComplete,
  className = "",
}: SlideGrowButtonProps) {
  const [isSlid, setIsSlid] = useState(false);

  const themeConfig = {
    silver: {
      bg: "bg-[#E6E8ED]",
      border: "border-white/80",
      channel: "bg-[#0077FF]",
      text: "text-[#333333]",
      slidText: "text-white",
    },
    uipirate: {
      bg: "bg-[#1C1815]",
      border: "border-orange-500/30",
      channel: "bg-[#FF5B04]",
      text: "text-[#F5F5F5]",
      slidText: "text-white",
    },
  }[theme as "silver" | "uipirate"] || {
    bg: "bg-[#E6E8ED]",
    border: "border-white/80",
    channel: "bg-[#0077FF]",
    text: "text-[#333333]",
    slidText: "text-white",
  };

  return (
    <div
      className={`relative inline-flex items-center select-none p-2 rounded-full ${themeConfig.bg} ${themeConfig.border} border shadow-xl ${className}`}
    >
      <div className="relative w-[230px] h-[52px] flex items-center justify-between px-6 rounded-full overflow-hidden">
        {/* Animated Slide Channel */}
        <motion.div
          animate={{ width: isSlid ? "100%" : "0%" }}
          transition={{ type: "spring", stiffness: 350, damping: 28 }}
          className={`absolute left-0 top-0 bottom-0 ${themeConfig.channel} rounded-full`}
        />

        {/* Text */}
        <span
          className={`relative z-10 font-bold text-sm transition-colors duration-200 ${
            isSlid ? themeConfig.slidText : themeConfig.text
          }`}
        >
          {isSlid ? activeLabel : startLabel}
        </span>

        {/* Drag / Click Slider Knob */}
        <motion.button
          type="button"
          onClick={() => {
            setIsSlid((s) => !s);
            if (!isSlid && onComplete) onComplete();
          }}
          animate={{ x: isSlid ? 0 : 0 }}
          className="relative z-10 w-10 h-10 rounded-full bg-white shadow-md flex items-center justify-center cursor-pointer focus:outline-none"
        >
          <svg
            className={`w-4 h-4 text-neutral-800 transition-transform duration-300 ${
              isSlid ? "rotate-180" : ""
            }`}
            fill="none"
            stroke="currentColor"
            viewBox="0 0 24 24"
          >
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M9 5l7 7-7 7" />
          </svg>
        </motion.button>
      </div>
    </div>
  );
}

Peer Dependencies

Requires Framer Motion and Tailwind CSS for spring physics and layout tokens:

npm install framer-motion clsx lucide-react tailwind-merge

API & Props Reference

PropTypeDefaultDescription
startLabelstring"Get Started"Resting text before slider interaction.
activeLabelstring"Lets Grow!"Revealed label when slid to completion.
theme"silver" | "dark" | "uipirate" | "cyberpunk" | "emerald" | "orange""silver"Metallic finish for the capsule track.
size"xs" | "sm" | "md" | "lg" | "xl""md"Width and height scaling dimensions.
interactionMode"both" | "drag" | "click" | "hover""both"Interaction trigger mode: drag, click, hover, or both.
onComplete() => voidundefinedCalled when swipe reaches 100% threshold.