shadimage
flowui
componentlift-buttonsource: ok · hit

Lift Button

A scroll-to-top button with custom triggers and target containers.

npx shadcn add "https://flowui-registry.vercel.app/r/lift-button.json"
free

No selection tags yet — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
iconslucide-react

Files1 · source cached 10/8/2026, 2:09:57 PM

registry/flowui/components/lift-button.tsx
"use client";

import { cn } from "@/lib/utils";
import { ArrowUp } from "lucide-react";
import { ComponentProps, useEffect, useState } from "react";

type LiftButtonProps = {
    className?: string;
    children?: React.ReactNode;
    scrollContainerId?: string;
    targetId?: string;
    alwaysVisible?: boolean;
    threshold?: number;
} & ComponentProps<"button">;

const LiftButton = ({
    className,
    children,
    scrollContainerId,
    targetId,
    alwaysVisible = false,
    threshold = 0,
    ...props
}: LiftButtonProps) => {
    // State to control the visibility
    const [visible, setVisible] = useState<boolean>(alwaysVisible);

    // Scroll based button visibility
    useEffect(() => {
        if (alwaysVisible) return;

        // Get the scroll container
        const container = scrollContainerId ? document.getElementById(scrollContainerId) : window;

        if (!container) return;

        const handleScroll = () => {
            const scrollPosition = container === window ?
                window.pageYOffset || document.documentElement.scrollTop :
                (container as HTMLElement).scrollTop;

            setVisible(scrollPosition > threshold);
        };

        container?.addEventListener("scroll", handleScroll, { passive: true });
        handleScroll();

        return () => container?.removeEventListener("scroll", handleScroll);
    }, [alwaysVisible, threshold, scrollContainerId]);

    // Scroll to top functionality
    const handleClick = () => {
        if(scrollContainerId) {
            const container = document.getElementById(scrollContainerId);
            if(!container) return;

            if(targetId) {
                const target = document.getElementById(targetId);
                if(!target) return;

                const containerRec = container.getBoundingClientRect();
                const targetRec = target.getBoundingClientRect();

                const offset = targetRec.top - containerRec.top + container.scrollTop;
                container.scrollTo({ top: offset, behavior: "smooth" });
            } else {
                container.scrollTo({ top: 0, behavior: "smooth" });
            }

            return;
        }
        if(targetId) {
            const target = document.getElementById(targetId);
            target?.scrollIntoView({ behavior: "smooth" });
            return;
        } else {
            window.scrollTo({ top: 0, behavior: "smooth" });
        }
    }

    return (
        <button
            className={cn(
                "inline-flex justify-center items-center transition-all duration-300",
                className,
                visible ? "opacity-100" : "opacity-0 pointer-events-none"
            )}
            {...props}
            onClick={handleClick}
        >
            {children ? (
                children
            ) : (
                <ArrowUp className="size-5" />
            )}
        </button>
    )
}

export default LiftButton;

More from flowui

Browse all components in this library →