1.0.41 • Published 12 months ago

use-stick-to-bottom v1.0.41

Weekly downloads
-
License
MIT
Repository
github
Last release
12 months ago

useStickToBottom

Designed with AI chat bots in mind, powering bolt.new by StackBlitz

npm version npm downloads Demo

A lightweight zero-dependency React hook + Component that automatically sticks to the bottom of container and smoothly animates the content to keep it's visual position on screen whilst new content is being added.

Features

  • Does not require overflow-anchor browser-level CSS support which Safari does not support.
  • Can be connected up to any existing component using a hook with refs. Or simply use the provided component, which handles the refs for you plus provides context - so child components can check isAtBottom & programmatically scroll to the bottom.
  • Uses the modern, yet well-supported, ResizeObserver API to detect when content resizes.
    • Supports content shrinking without losing stickiness - not just getting taller.
  • Correctly handles Scroll Anchoring. This is where when content above the viewport resizes, it doesn't cause the content currently displayed in viewport to jump up or down.
  • Allows the user to cancel the stickiness at any time by scrolling up.
    • Clever logic distinguishes the user scrolling from the custom animation scroll events (without doing any debouncing which could cause some events to be missed).
    • Mobile devices work well with this logic too.
  • Uses a custom implemented smooth scrolling algorithm, featuring velocity-based spring animations (with configurable parameters).
    • Other libraries use easing functions with durations instead, but these doesn't work well when you want to stream in new content with variable sizing - which is common for AI chatbot use cases.
    • scrollToBottom returns a Promise<boolean> which will resolve to true as soon as the scroll was successful, or false if the scroll was cancelled.

Usage

<StickToBottom> Component

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()}
      />
    )
  );
}

useStickToBottom Hook

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>
  );
}
1.0.41

12 months ago

1.0.40

1 year ago

1.0.39

1 year ago

1.0.38

1 year ago

1.0.37

1 year ago

1.0.36

1 year ago

1.0.35

1 year ago

1.0.34

1 year ago

1.0.33

1 year ago

1.0.32

1 year ago

1.0.31

1 year ago

1.0.30

1 year ago

1.0.29

1 year ago

1.0.28

1 year ago

1.0.27

1 year ago

1.0.26

1 year ago

1.0.25

1 year ago

1.0.24

1 year ago

1.0.23

1 year ago

1.0.22

1 year ago

1.0.21

1 year ago

1.0.20

1 year ago

1.0.19

1 year ago

1.0.18

1 year ago

1.0.17

1 year ago

1.0.16

1 year ago

1.0.15

1 year ago

1.0.14

1 year ago

1.0.13

1 year ago

1.0.12

1 year ago

1.0.11

1 year ago

1.0.10

1 year ago

1.0.9

1 year ago

1.0.8

1 year ago

1.0.7

1 year ago

1.0.6

1 year ago

1.0.5

1 year ago

1.0.4

1 year ago

1.0.3

1 year ago

1.0.2

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago

0.0.1

1 year ago