npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vue-stick-to-bottom

v1.0.0

Published

A lightweight Vue utility intended mainly for AI chat applications, for smoothly sticking to the bottom of messages.

Readme

vue-stick-to-bottom

Designed with AI chat bots in mind, unofficial Vue port of use-stick-to-bottom.

npm version npm downloads

A lightweight Vue 3 component and composable that automatically sticks to the bottom of a scrollable container and smoothly animates content to keep its visual position on screen while new content is being added.

Features

  • Does not require overflow-anchor browser-level CSS support (which Safari does not support).
  • Can be connected to any existing component using a composable with refs, or simply use the provided component which handles refs for you plus provides context via provide/inject.
  • 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. When content above the viewport resizes, the content currently displayed in the viewport won't jump up or down.
  • Allows the user to cancel the stickiness at any time by scrolling up.
    • Clever logic distinguishes user scrolling from custom animation scroll events (without debouncing which could cause some events to be missed).
    • Works well on mobile devices too.
  • Uses a custom smooth scrolling algorithm with velocity-based spring animations (configurable parameters).
    • Other libraries use easing functions with durations, which don't work well when streaming in new content with variable sizing — common for AI chatbot use cases.
    • scrollToBottom returns a Promise<boolean> which resolves to true once the scroll succeeds, or false if the scroll was cancelled.
  • Zero runtime dependencies.
  • Tree-shakable ESM build with full TypeScript declarations.

Installation

pnpm add vue-stick-to-bottom
# or: npm install / yarn add

Usage

<StickToBottom> Component

<script setup lang="ts">
import { StickToBottom, useStickToBottomContext } from 'vue-stick-to-bottom'
</script>

<template>
  <StickToBottom class="h-[50vh] relative" resize="smooth" :initial="{ mass: 10 }">
    <div class="flex flex-col gap-4">
      <Message v-for="msg in messages" :key="msg.id" :message="msg" />
    </div>

    <template #overlay>
      <ScrollToBottom />
    </template>

    <template #after>
      <ChatBox />
    </template>
  </StickToBottom>
</template>

The ScrollToBottom child component can use useStickToBottomContext to access state and actions:

<script setup lang="ts">
import { useStickToBottomContext } from 'vue-stick-to-bottom'

const { isAtBottom, scrollToBottom } = useStickToBottomContext()
</script>

<template>
  <button
    v-if="!isAtBottom"
    class="absolute bottom-2 left-1/2 -translate-x-1/2"
    @click="scrollToBottom()"
  >
    ↓ Scroll to bottom
  </button>
</template>

useStickToBottom Composable

For full control over the DOM structure, use the composable directly:

<script setup lang="ts">
import { useStickToBottom } from 'vue-stick-to-bottom'

const { scrollRef, contentRef, isAtBottom, scrollToBottom } = useStickToBottom()
</script>

<template>
  <div ref="scrollRef" style="overflow: auto; height: 50vh;">
    <div ref="contentRef">
      <Message v-for="msg in messages" :key="msg.id" :message="msg" />
    </div>
  </div>

  <button v-if="!isAtBottom" @click="scrollToBottom()">↓</button>
</template>

Slot Props

All slots (default, overlay, after) expose the following props for convenience:

<StickToBottom>
  <template #default="{ isAtBottom, scrollToBottom }">
    <!-- Use slot props directly without useStickToBottomContext -->
  </template>
</StickToBottom>

API

<StickToBottom> Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | resize | Animation | Spring | Animation to use when content resizes. | | initial | Animation \| boolean | Spring | Animation for initial scroll. false to start at top. | | targetScrollTop | GetTargetScrollTop | — | Custom function to compute the target scroll position. | | damping | number | 0.7 | Spring damping (0–1). 0 = no damping, 1 = full. | | stiffness | number | 0.05 | Spring stiffness — how fast the animation accelerates. | | mass | number | 1.25 | Spring mass — higher = slower animation. | | anchor | 'auto' \| 'none' | 'auto' | Maps to CSS overflow-anchor. Use 'none' to disable browser scroll anchoring. |

Animation type

type Animation = ScrollBehavior | SpringAnimation
// ScrollBehavior: 'auto' | 'instant' | 'smooth'
// SpringAnimation: { damping?: number; stiffness?: number; mass?: number }

<StickToBottom> Slots

| Slot | Description | |------|-------------| | default | Content inside the scrollable area. | | overlay | Floats above the scrollable content (e.g. scroll-to-bottom button). Positioned within the relative wrapper. | | after | Outside the scroll area entirely (e.g. input box, external controls). |

All slots receive { isAtBottom, isNearBottom, escapedFromLock, scrollToBottom, stopScroll } as slot props.

useStickToBottom(options?)

Returns:

| Property | Type | Description | |----------|------|-------------| | scrollRef | Ref<HTMLElement \| null> | Template ref for the scroll container. | | contentRef | Ref<HTMLElement \| null> | Template ref for the content wrapper. | | isAtBottom | Ref<boolean> | Whether the container is at (or very near) the bottom. | | isNearBottom | Ref<boolean> | Whether the container is within ~70px of the bottom. | | escapedFromLock | Ref<boolean> | Whether the user has scrolled up to cancel stickiness. | | scrollToBottom | ScrollToBottom | Programmatically scroll to the bottom. | | stopScroll | StopScroll | Stop the current scroll animation and release the lock. | | setOptions | (options) => void | Update engine options at runtime. |

scrollToBottom(options?)

type ScrollToBottomOptions =
  | ScrollBehavior
  | {
      animation?: Animation
      wait?: boolean | number     // Wait for existing scrolls or delay in ms
      ignoreEscapes?: boolean     // Prevent user from cancelling this scroll
      preserveScrollPosition?: boolean  // Only scroll if already at bottom
      duration?: number | Promise<void> // Extra duration to maintain the scroll
    }

Returns Promise<boolean> — resolves to true if scroll completed, false if cancelled.

useStickToBottomContext()

Call inside a child component of <StickToBottom> to access the context via inject. Throws if used outside the component tree.

Demo

Run the built-in demo:

pnpm dev

This opens a playground showcasing smooth vs instant columns, a speed slider, a floating scroll-to-bottom button, and stop-scroll controls.

Credits

This is a Vue 3 port of use-stick-to-bottom by Sam Denty / StackBlitz.

License

MIT