{"_id":"vue-stick-to-bottom","_rev":"3-cba57c4773f1d6458b8d0be19984eb84","name":"vue-stick-to-bottom","dist-tags":{"latest":"1.0.1"},"versions":{"0.1.0":{"name":"vue-stick-to-bottom","version":"0.1.0","keywords":["vue","vue3","stick-to-bottom","scroll","chat","auto-scroll","scroll-to-bottom","resizeobserver","infinite-list","ui"],"author":{"url":"https://github.com/cwandev","name":"Wang Chao"},"license":"MIT","_id":"vue-stick-to-bottom@0.1.0","maintainers":[{"name":"c_wang","email":"18888351756@163.com"}],"homepage":"https://github.com/cwandev/vue-stick-to-bottom#readme","bugs":{"url":"https://github.com/cwandev/vue-stick-to-bottom/issues"},"dist":{"shasum":"06dd629ffa43faf60ee21bf09eb2604ce04a0896","tarball":"https://registry.npmjs.org/vue-stick-to-bottom/-/vue-stick-to-bottom-0.1.0.tgz","fileCount":7,"integrity":"sha512-qe2EQoVdmEfxW4FKJdiqPSXa2ZZe9jmVjglHEiRKWdQVYf5MUYT/Yl73QrvOqNdgIl9EXBg50OAaHOIeHa17vg==","signatures":[{"sig":"MEUCIHH4KqBKpu6/krnav68RAAIy/f9C2fLP4ZLZlBmWVbR+AiEAws39DY4m0F8LQXaH592VwHiDdfhB31tG4OjxFV2iRUM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":61135},"main":"dist/index.es.js","type":"module","_from":"file:vue-stick-to-bottom-0.1.0.tgz","types":"dist/index.d.ts","module":"dist/index.es.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.es.js"}},"private":false,"scripts":{"dev":"vite","lint":"eslint","release":"bumpp --commit --push --tag","lint:fix":"eslint --fix","build:lib":"vite build -c vite.lib.config.ts","build:demo":"vite build","preview:demo":"vite preview --host"},"_npmUser":{"name":"c_wang","email":"18888351756@163.com"},"_resolved":"/private/var/folders/79/hsh3gbh52731rlr4fqpflm9h0000gn/T/ee077bd4aab589e7fbe88b5eb11a357e/vue-stick-to-bottom-0.1.0.tgz","_integrity":"sha512-qe2EQoVdmEfxW4FKJdiqPSXa2ZZe9jmVjglHEiRKWdQVYf5MUYT/Yl73QrvOqNdgIl9EXBg50OAaHOIeHa17vg==","commitlint":{"extends":["@commitlint/config-conventional"]},"repository":{"url":"git+https://github.com/cwandev/vue-stick-to-bottom.git","type":"git"},"_npmVersion":"10.9.2","description":"A lightweight Vue utility intended mainly for AI chat applications, for smoothly sticking to the bottom of messages.","directories":{},"lint-staged":{"*":"eslint --fix"},"sideEffects":false,"_nodeVersion":"22.17.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.5.18","vite":"^7.1.2","bumpp":"^10.2.3","eslint":"^9.34.0","vue-tsc":"^3.0.5","typescript":"~5.8.3","@types/node":"^24.3.1","lint-staged":"^16.1.6","lorem-ipsum":"^2.0.8","tailwindcss":"^4.1.13","@vue/tsconfig":"^0.7.0","@commitlint/cli":"^19.8.1","vite-plugin-dts":"^4.5.4","@iconify-json/ph":"^1.2.2","simple-git-hooks":"^2.13.1","@tailwindcss/vite":"^4.1.13","@vitejs/plugin-vue":"^6.0.1","@antfu/eslint-config":"^5.2.2","@tailwindcss/typography":"^0.5.16","@egoist/tailwindcss-icons":"^1.9.0","@commitlint/config-conventional":"^19.8.1"},"peerDependencies":{"vue":">=3.3.0"},"simple-git-hooks":{"commit-msg":"pnpm commitlint --edit ${1}","pre-commit":"pnpm lint-staged"},"_npmOperationalInternal":{"tmp":"tmp/vue-stick-to-bottom_0.1.0_1757132621702_0.3054287101307329","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"vue-stick-to-bottom","version":"1.0.0","keywords":["vue","vue3","stick-to-bottom","scroll","chat","auto-scroll","scroll-to-bottom","resizeobserver","infinite-list","ui"],"author":{"url":"https://github.com/cwandev","name":"Wang Chao"},"license":"MIT","_id":"vue-stick-to-bottom@1.0.0","maintainers":[{"name":"c_wang","email":"18888351756@163.com"}],"homepage":"https://github.com/cwandev/vue-stick-to-bottom#readme","bugs":{"url":"https://github.com/cwandev/vue-stick-to-bottom/issues"},"dist":{"shasum":"371bb09e14911b7fbd983e2970b5c2e8d3098daa","tarball":"https://registry.npmjs.org/vue-stick-to-bottom/-/vue-stick-to-bottom-1.0.0.tgz","fileCount":7,"integrity":"sha512-e6OrT4DGsIEZoQ2wiqBJ61j1CmHJVY4dc8TJrndqbuAE+/m2XQ7fzDyakCu2GCxU2qePOcJivYf9Xua54fC6EA==","signatures":[{"sig":"MEQCIE8KtTsD6VNqa+69DJwX4FK3HEIH160SI2WLiahDMZEVAiBOq+RzlYpo6UsRMeXIskBJNGeunHvlyK2BdCTLI73P8w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":67185},"main":"dist/index.es.js","type":"module","_from":"file:vue-stick-to-bottom-1.0.0.tgz","types":"dist/index.d.ts","module":"dist/index.es.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.es.js"}},"private":false,"scripts":{"dev":"vite","lint":"eslint","release":"bumpp --commit --push --tag","lint:fix":"eslint --fix","build:lib":"vite build -c vite.lib.config.ts","build:demo":"vite build","preview:demo":"vite preview --host"},"_npmUser":{"name":"c_wang","email":"18888351756@163.com"},"_resolved":"/tmp/ad4da925eba3bff605786d964316b89a/vue-stick-to-bottom-1.0.0.tgz","_integrity":"sha512-e6OrT4DGsIEZoQ2wiqBJ61j1CmHJVY4dc8TJrndqbuAE+/m2XQ7fzDyakCu2GCxU2qePOcJivYf9Xua54fC6EA==","commitlint":{"extends":["@commitlint/config-conventional"]},"repository":{"url":"git+https://github.com/cwandev/vue-stick-to-bottom.git","type":"git"},"_npmVersion":"10.8.2","description":"A lightweight Vue utility intended mainly for AI chat applications, for smoothly sticking to the bottom of messages.","directories":{},"lint-staged":{"*":"eslint --fix"},"sideEffects":false,"_nodeVersion":"20.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.5.29","vite":"^7.3.1","bumpp":"^10.4.1","eslint":"^10.0.2","vue-tsc":"^3.2.5","typescript":"^5.9.3","@types/node":"^25.3.1","lint-staged":"^16.2.7","lorem-ipsum":"^2.0.8","tailwindcss":"^4.2.1","@vue/tsconfig":"^0.8.1","@commitlint/cli":"^20.4.2","vite-plugin-dts":"^4.5.4","@iconify-json/ph":"^1.2.2","simple-git-hooks":"^2.13.1","@tailwindcss/vite":"^4.2.1","@vitejs/plugin-vue":"^6.0.4","@antfu/eslint-config":"^7.6.1","@tailwindcss/typography":"^0.5.19","@egoist/tailwindcss-icons":"^1.9.2","@commitlint/config-conventional":"^20.4.2"},"peerDependencies":{"vue":">=3.3.0"},"simple-git-hooks":{"commit-msg":"pnpm commitlint --edit ${1}","pre-commit":"pnpm lint-staged"},"_npmOperationalInternal":{"tmp":"tmp/vue-stick-to-bottom_1.0.0_1772126823895_0.4557566883989037","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"_id":"vue-stick-to-bottom@1.0.1","bugs":{"url":"https://github.com/cwandev/vue-stick-to-bottom/issues"},"dist":{"shasum":"416c7a1836ae930a6d5d9da09afa30df203c9a16","tarball":"https://registry.npmjs.org/vue-stick-to-bottom/-/vue-stick-to-bottom-1.0.1.tgz","integrity":"sha512-RuT83MqGsQuBwCYq3KOlsXIz9u2ITE5iToJyM6iOOT17gvxaCjgU6lvOCjbRNVwejy7KxSy5EZG9UOuR+WkEVA==","fileCount":7,"unpackedSize":66210,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/vue-stick-to-bottom@1.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBADNLpvCUSTZgSCUG12sZhCNb/H5BG1eZZEPpuVtRNUAiEApvosBncoZei6WWA+oewTKsCBeqDPt8qAnN1faD0rS2w="}]},"main":"dist/index.es.js","name":"vue-stick-to-bottom","type":"module","types":"dist/index.d.ts","author":{"url":"https://github.com/cwandev","name":"Wang Chao"},"module":"dist/index.es.js","engines":{"node":">=22.22.0","pnpm":">=11.15.1"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.es.js"}},"license":"MIT","private":false,"scripts":{"dev":"vite","lint":"eslint","release":"bumpp --commit --push --tag","lint:fix":"eslint --fix","build:lib":"vite build -c vite.lib.config.ts","build:demo":"vite build","preview:demo":"vite preview --host"},"version":"1.0.1","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:f1167d0d-d37a-488d-8c22-b64fdf3e12d1"},"approver":{"name":"c_wang","email":"18888351756@163.com"}},"homepage":"https://vue-stick-to-bottom.netlify.app/","keywords":["vue","vue3","stick-to-bottom","scroll","chat","auto-scroll","scroll-to-bottom","resizeobserver","infinite-list","ui"],"commitlint":{"extends":["@commitlint/config-conventional"]},"repository":{"url":"git+https://github.com/cwandev/vue-stick-to-bottom.git","type":"git"},"description":"A lightweight Vue utility intended mainly for AI chat applications, for smoothly sticking to the bottom of messages.","directories":{},"lint-staged":{"*":"eslint --fix"},"maintainers":[{"name":"c_wang","email":"18888351756@163.com"}],"sideEffects":false,"_nodeVersion":"24.18.0","publishConfig":{"access":"public"},"devDependencies":{"vue":"^3.5.40","vite":"^8.1.5","bumpp":"^12.0.0","eslint":"^10.7.0","vue-tsc":"^3.3.7","typescript":"^5.9.3","@types/node":"^26.1.1","lint-staged":"^17.1.0","lorem-ipsum":"^3.0.0","tailwindcss":"^4.3.3","unplugin-dts":"^1.0.3","@vue/tsconfig":"^0.9.1","@commitlint/cli":"^21.2.1","@iconify-json/ph":"^1.2.2","simple-git-hooks":"^2.13.1","@tailwindcss/vite":"^4.3.3","@vitejs/plugin-vue":"^6.0.8","@vue/language-core":"^3.3.7","@antfu/eslint-config":"^9.1.0","@tailwindcss/typography":"^0.5.20","@microsoft/api-extractor":"^7.58.12","@egoist/tailwindcss-icons":"^1.9.2","@commitlint/config-conventional":"^21.2.0"},"peerDependencies":{"vue":">=3.3.0"},"simple-git-hooks":{"commit-msg":"pnpm commitlint --edit ${1}","pre-commit":"pnpm lint-staged"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/vue-stick-to-bottom_1.0.1_1784717751045_0.2723092004151908"},"_hasShrinkwrap":false}},"time":{"created":"2025-09-06T04:23:41.620Z","modified":"2026-07-22T10:55:51.445Z","0.1.0":"2025-09-06T04:23:41.909Z","1.0.0":"2026-02-26T17:27:04.042Z","1.0.1":"2026-07-22T10:55:51.164Z"},"bugs":{"url":"https://github.com/cwandev/vue-stick-to-bottom/issues"},"author":{"url":"https://github.com/cwandev","name":"Wang Chao"},"license":"MIT","homepage":"https://vue-stick-to-bottom.netlify.app/","keywords":["vue","vue3","stick-to-bottom","scroll","chat","auto-scroll","scroll-to-bottom","resizeobserver","infinite-list","ui"],"repository":{"url":"git+https://github.com/cwandev/vue-stick-to-bottom.git","type":"git"},"description":"A lightweight Vue utility intended mainly for AI chat applications, for smoothly sticking to the bottom of messages.","maintainers":[{"name":"c_wang","email":"18888351756@163.com"}],"readme":"# vue-stick-to-bottom\n\n> Designed with AI chat bots in mind, unofficial Vue port of [use-stick-to-bottom](https://github.com/stackblitz-labs/use-stick-to-bottom).\n\n[![npm version](https://img.shields.io/npm/v/vue-stick-to-bottom.svg?style=flat-square)](https://www.npmjs.com/package/vue-stick-to-bottom)\n[![npm downloads](https://img.shields.io/npm/dm/vue-stick-to-bottom.svg?style=flat-square)](https://www.npmjs.com/package/vue-stick-to-bottom)\n\nA 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.\n\n## Features\n\n- Does not require [`overflow-anchor`](https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-anchor) browser-level CSS support (which Safari does not support).\n- 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`.\n- Uses the modern, yet well-supported, [`ResizeObserver`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) API to detect when content resizes.\n  - Supports content shrinking without losing stickiness — not just getting taller.\n- Correctly handles [Scroll Anchoring](https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-anchor/Guide_to_scroll_anchoring). When content above the viewport resizes, the content currently displayed in the viewport won't jump up or down.\n- Allows the user to cancel the stickiness at any time by scrolling up.\n  - Clever logic distinguishes user scrolling from custom animation scroll events (without debouncing which could cause some events to be missed).\n  - Works well on mobile devices too.\n- Uses a custom smooth scrolling algorithm with velocity-based spring animations (configurable parameters).\n  - 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.\n  - `scrollToBottom` returns a `Promise<boolean>` which resolves to `true` once the scroll succeeds, or `false` if the scroll was cancelled.\n- Zero runtime dependencies.\n- Tree-shakable ESM build with full TypeScript declarations.\n\n## Installation\n\n```bash\npnpm add vue-stick-to-bottom\n# or: npm install / yarn add\n```\n\n## Usage\n\n### `<StickToBottom>` Component\n\n```vue\n<script setup lang=\"ts\">\nimport { StickToBottom, useStickToBottomContext } from 'vue-stick-to-bottom'\n</script>\n\n<template>\n  <StickToBottom class=\"h-[50vh] relative\" resize=\"smooth\" :initial=\"{ mass: 10 }\">\n    <div class=\"flex flex-col gap-4\">\n      <Message v-for=\"msg in messages\" :key=\"msg.id\" :message=\"msg\" />\n    </div>\n\n    <template #overlay>\n      <ScrollToBottom />\n    </template>\n\n    <template #after>\n      <ChatBox />\n    </template>\n  </StickToBottom>\n</template>\n```\n\nThe `ScrollToBottom` child component can use `useStickToBottomContext` to access state and actions:\n\n```vue\n<script setup lang=\"ts\">\nimport { useStickToBottomContext } from 'vue-stick-to-bottom'\n\nconst { isAtBottom, scrollToBottom } = useStickToBottomContext()\n</script>\n\n<template>\n  <button\n    v-if=\"!isAtBottom\"\n    class=\"absolute bottom-2 left-1/2 -translate-x-1/2\"\n    @click=\"scrollToBottom()\"\n  >\n    ↓ Scroll to bottom\n  </button>\n</template>\n```\n\n### `useStickToBottom` Composable\n\nFor full control over the DOM structure, use the composable directly:\n\n```vue\n<script setup lang=\"ts\">\nimport { useStickToBottom } from 'vue-stick-to-bottom'\n\nconst { scrollRef, contentRef, isAtBottom, scrollToBottom } = useStickToBottom()\n</script>\n\n<template>\n  <div ref=\"scrollRef\" style=\"overflow: auto; height: 50vh;\">\n    <div ref=\"contentRef\">\n      <Message v-for=\"msg in messages\" :key=\"msg.id\" :message=\"msg\" />\n    </div>\n  </div>\n\n  <button v-if=\"!isAtBottom\" @click=\"scrollToBottom()\">↓</button>\n</template>\n```\n\n### Slot Props\n\nAll slots (`default`, `overlay`, `after`) expose the following props for convenience:\n\n```vue\n<StickToBottom>\n  <template #default=\"{ isAtBottom, scrollToBottom }\">\n    <!-- Use slot props directly without useStickToBottomContext -->\n  </template>\n</StickToBottom>\n```\n\n## API\n\n### `<StickToBottom>` Props\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `resize` | `Animation` | Spring | Animation to use when content resizes. |\n| `initial` | `Animation \\| boolean` | Spring | Animation for initial scroll. `false` to start at top. |\n| `targetScrollTop` | `GetTargetScrollTop` | — | Custom function to compute the target scroll position. |\n| `damping` | `number` | `0.7` | Spring damping (0–1). 0 = no damping, 1 = full. |\n| `stiffness` | `number` | `0.05` | Spring stiffness — how fast the animation accelerates. |\n| `mass` | `number` | `1.25` | Spring mass — higher = slower animation. |\n| `anchor` | `'auto' \\| 'none'` | `'auto'` | Maps to CSS `overflow-anchor`. Use `'none'` to disable browser scroll anchoring. |\n\n#### `Animation` type\n\n```typescript\ntype Animation = ScrollBehavior | SpringAnimation\n// ScrollBehavior: 'auto' | 'instant' | 'smooth'\n// SpringAnimation: { damping?: number; stiffness?: number; mass?: number }\n```\n\n### `<StickToBottom>` Slots\n\n| Slot | Description |\n|------|-------------|\n| `default` | Content inside the scrollable area. |\n| `overlay` | Floats above the scrollable content (e.g. scroll-to-bottom button). Positioned within the relative wrapper. |\n| `after` | Outside the scroll area entirely (e.g. input box, external controls). |\n\nAll slots receive `{ isAtBottom, isNearBottom, escapedFromLock, scrollToBottom, stopScroll }` as slot props.\n\n### `useStickToBottom(options?)`\n\nReturns:\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `scrollRef` | `Ref<HTMLElement \\| null>` | Template ref for the scroll container. |\n| `contentRef` | `Ref<HTMLElement \\| null>` | Template ref for the content wrapper. |\n| `isAtBottom` | `Ref<boolean>` | Whether the container is at (or very near) the bottom. |\n| `isNearBottom` | `Ref<boolean>` | Whether the container is within ~70px of the bottom. |\n| `escapedFromLock` | `Ref<boolean>` | Whether the user has scrolled up to cancel stickiness. |\n| `scrollToBottom` | `ScrollToBottom` | Programmatically scroll to the bottom. |\n| `stopScroll` | `StopScroll` | Stop the current scroll animation and release the lock. |\n| `setOptions` | `(options) => void` | Update engine options at runtime. |\n\n### `scrollToBottom(options?)`\n\n```typescript\ntype ScrollToBottomOptions =\n  | ScrollBehavior\n  | {\n      animation?: Animation\n      wait?: boolean | number     // Wait for existing scrolls or delay in ms\n      ignoreEscapes?: boolean     // Prevent user from cancelling this scroll\n      preserveScrollPosition?: boolean  // Only scroll if already at bottom\n      duration?: number | Promise<void> // Extra duration to maintain the scroll\n    }\n```\n\nReturns `Promise<boolean>` — resolves to `true` if scroll completed, `false` if cancelled.\n\n### `useStickToBottomContext()`\n\nCall inside a child component of `<StickToBottom>` to access the context via `inject`. Throws if used outside the component tree.\n\n## Demo\n\nRun the built-in demo:\n\n```bash\npnpm dev\n```\n\nThis opens a playground showcasing smooth vs instant columns, a speed slider, a floating scroll-to-bottom button, and stop-scroll controls.\n\n## Credits\n\nThis is a Vue 3 port of [use-stick-to-bottom](https://github.com/stackblitz-labs/use-stick-to-bottom) by [Sam Denty](https://github.com/samdenty) / StackBlitz.\n\n## License\n\n[MIT](./LICENSE)\n","readmeFilename":""}