Install use-stick-to-bottom via npm
mainInstall the package using npm to add the use-stick-to-bottom hook and component to your React project.
npm install use-stick-to-bottomrepository·main·Indexed 20 days ago
https://github.com/stackblitz-labs/use-stick-to-bottomA lightweight, zero-dependency React library designed for AI chatbot interfaces. It provides a hook and component to automatically stick to the bottom of a container and smoothly animate content to maintain visual position when new content is added. Includes the <StickToBottom> component for declarative implementation and the useStickToBottom hook for custom programmatic control.
Install the package using npm to add the use-stick-to-bottom hook and component to your React project.
npm install use-stick-to-bottomThe <StickToBottom> component acts as a provider that manages scroll stickiness logic. It can be used in two ways:
children to access the StickToBottomContext directly.<StickToBottom.Content> sub-component inside <StickToBottom> to automatically wire up the necessary scroll and content refs.| Prop | Type | Description |
|---|---|---|
mass | number | Physics mass for the spring animation |
damping | number | Physics damping for the spring animation |
stiffness | number | Physics stiffness for the spring animation |
resize | boolean | Whether to resize on window resize |
initial | boolean | Whether to start in a stick-to-bottom state |
targetScrollTop | GetTargetScrollTop | A function to customize how the target scroll position is calculated |
instance | StickToBottomInstance | An existing instance to use instead of creating a new one |
contextRef | React.Ref<StickToBottomContext> | A ref to access the context object imperatively |
children | ((context: StickToBottomContext) => ReactNode) | ReactNode | The content to render, optionally as a function receiving the context |
import { StickToBottom } from 'use-stick-to-bottom';
function MyComponent() {
return (
<StickToBottom resize damping={20}>
<StickToBottom.Content>
{({ scrollToBottom, isAtBottom }) => (
<div>
<button onClick={scrollToBottom}>Scroll to bottom</button>
<p>Is at bottom: {isAtBottom ? 'Yes' : 'No'}</p>
<div style={{ height: '1000px' }}>Long Content</div>
</div>
)}
</StickToBottom.Content>
</StickToBottom>
);
}The <StickToBottom> component is the easiest way to implement stick-to-bottom behavior. It manages refs for you and provides a context that child components can consume to check if the user is at the bottom or to trigger a scroll.
Key props:
resize: Controls how the component handles content resizing (e.g., "smooth").initial: Controls the initial scroll behavior (e.g., "smooth").Sub-components:
<StickToBottom.Content>: Wraps the content that should be tracked for resizing and scrolling.import { StickToBottom, useStickToBottomContext } from 'use-stick-to-bottom';
function Chat() {
return (
<StickToBottom className="h-[50vh] relative" resize="smooth" initial="smooth">
<StickToBottom.Content className="flex flex-col gap-4">
{messages.map((message) => (
<Message key={message.id} message={message} />
))}
</StickToBottom.Content>
<ScrollToBottom />
{/* This component uses `useStickToBottomContext` to scroll to bottom when the user enters a message */}
<ChatBox />
</StickToBottom>
);
}
function ScrollToBottom() {
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
return (
!isAtBottom && (
<button
className="absolute i-ph-arrow-circle-down-fill text-4xl rounded-lg left-[50%] translate-x-[-50%] bottom-0"
onClick={() => scrollToBottom()}
/>
)
);
}If you need to integrate stick-to-bottom behavior into an existing component structure without using the provided component wrapper, use the useStickToBottom hook. You must manually attach the returned refs to your container and content elements.
Returns:
scrollRef: Attach this to the scrollable container (the element with overflow: auto or scroll).contentRef: Attach this to the inner content element that holds the items being added.import { useStickToBottom } from 'use-stick-to-bottom';
function Component() {
const { scrollRef, contentRef } = useStickToBottom();
return (
<div style={{ overflow: 'auto' }} ref={scrollRef}>
<div ref={contentRef}>
{messages.map((message) => (
<Message key={message.id} message={message} />
))}
</div>
</div>
);
}When using the <StickToBottom> component, child components can use the useStickToBottomContext hook to interact with the scroll state.
Available properties:
isAtBottom: A boolean indicating if the user is currently at the bottom of the container.scrollToBottom: A function that triggers a smooth scroll to the bottom. It returns a Promise<boolean> which resolves to true if the scroll was successful, or false if the scroll was cancelled (e.g., by the user scrolling up).When initializing useStickToBottom, you can pass a StickToBottomOptions object to customize behavior.
resize: An Animation type defining how the container scrolls when the content size changes.initial: An Animation or boolean defining the initial scroll behavior when the component mounts.targetScrollTop: A function (targetScrollTop: number, context: ScrollElements) => number that allows you to override the calculated target scroll position.If you use a spring animation instead of 'instant', you can tune the following:
damping: (0 to 1) How much to damp the animation. 0 is no damping, 1 is full damping. Default: 0.7.stiffness: How fast/slow the animation gets up to speed. Default: 0.05.mass: The inertial mass. Higher numbers make the animation slower. Default: 1.25.const { scrollRef, contentRef } = useStickToBottom({
resize: {
damping: 0.8,
stiffness: 0.1,
mass: 1.0
},
initial: 'instant'
});The useStickToBottom hook provides programmatic control over a scrollable container to keep it stuck to the bottom (e.g., for chat windows). It returns refs to attach to the scroll container and the content element, along with methods to trigger scrolling and state information.
To use it, you must provide two refs:
scrollRef: Attach this to the element that has the overflow/scroll property.contentRef: Attach this to the element containing the actual content that changes size.import { useStickToBottom } from 'use-stick-to-bottom';
function ChatWindow() {
const {
scrollRef,
contentRef,
scrollToBottom,
isAtBottom
} = useStickToBottom();
const handleNewMessage = async () => {
// ... add message to state
await scrollToBottom();
};
return (
<div ref={scrollRef} style={{ overflowY: 'auto', height: '400px' }}>
<div ref={contentRef}>
{/* Messages go here */}
</div>
</div>
);
}The hook returns several properties to help you react to the scroll state:
isAtBottom: boolean. True if the user is at the bottom or near the bottom (within 70px).isNearBottom: boolean. True if the user is within the STICK_TO_BOTTOM_OFFSET_PX (70px) threshold.escapedFromLock: boolean. True if the user has manually scrolled up, breaking the 'stick to bottom' behavior.scrollRef: A ref to be attached to the scrollable element.contentRef: A ref to be attached to the content element.state: The raw StickToBottomState object containing low-level metrics like scrollTop, velocity, and scrollDifference.If you are building custom components that need to live inside a <StickToBottom> tree, use the useStickToBottomContext hook to access the current scroll state and control methods.
Note: This hook must be called within a component that is a descendant of a <StickToBottom> component.
import { useStickToBottomContext } from 'use-stick-to-bottom';
function MySubComponent() {
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
return (
<button onClick={scrollToBottom} disabled={isAtBottom}>
Jump to Bottom
</button>
);
}If a programmatic scroll animation is currently in progress, you can call stopScroll() to immediately halt it and release the 'lock' (setting isAtBottom to false and escapedFromLock to true).
const { stopScroll } = useStickToBottom();
// Later, if needed:
stopScroll();The <StickToBottom.Content> component is a specialized wrapper designed to be used inside a <StickToBottom> provider. It automatically handles the assignment of scrollRef (to the outer scrollable container) and contentRef (to the inner content container) required for the stick-to-bottom logic to function.
It applies height: 100%, width: 100%, and scrollbar-gutter: stable both-edges to the scroll container to prevent layout shifts when scrollbars appear/disappear.
<StickToBottom>
<StickToBottom.Content scrollClassName="my-scroll-container">
{/* Your content here */}
</StickToBottom.Content>
</StickToBottom>The library provides two primary ways to implement 'stick to bottom' behavior: the useStickToBottom hook for manual control and the <StickToBottom> component for a declarative approach. Both are exported from the main entrypoint.
import { useStickToBottom, StickToBottom } from 'use-stick-to-bottom';