Vue3 Smooth Scroll
vue3-smooth-scroll is a lightweight, actively maintained Vue 3 plugin dedicated to facilitating smooth scrolling experiences within web applications. It provides two primary methods for implementation: a declarative directive (`v-smooth-scroll`) that can be applied directly to anchor tags, and a more flexible programmatic API accessible through `this.$smoothScroll` in the Options API or `inject('smoothScroll')` within the Composition API. The plugin's design prioritizes Vue 3 compatibility, including robust Server-Side Rendering (SSR) support, which was recently improved in version 0.8.1 to address `window is not defined` errors. It uses `requestAnimationFrame` for efficient, non-blocking animations, with a graceful fallback for broader compatibility across different browser environments. Key features include Y-axis scrolling, the ability to define specific scroll containers, configurable animation duration, offset, and custom easing functions, providing a high degree of customization for scroll behavior. Its small bundle size (approximately 1.4kB gzipped) makes it a performant choice, offering significantly more advanced control and a programmatic interface compared to the native `scroll-behavior` CSS property, especially for intricate scrolling requirements or dynamic content. Releases appear to be driven by bug fixes and compatibility updates rather than a strict schedule, with 0.8.1 being the current stable version.
Common errors
-
window is not defined
cause Attempting to execute client-side DOM or window-related JavaScript code in a Server-Side Rendering (SSR) environment without proper checks.fixUpdate `vue3-smooth-scroll` to version `0.8.1` or newer, as this version specifically addresses SSR compatibility. If using an older version or if the error persists with custom logic, ensure client-side code is conditionally executed (`if (typeof window !== 'undefined')`) or wrapped in SSR-aware components (e.g., `<ClientOnly>` in Nuxt).
Warnings
- gotcha SSR environments on versions prior to v0.8.1 may encounter `window is not defined` errors when the scrolling logic attempts to access browser-specific APIs.
- gotcha For very simple, static smooth scrolling needs, the native CSS `scroll-behavior: smooth` property can provide a lightweight alternative without a JavaScript dependency, potentially improving performance slightly.
Install
-
npm install vue3-smooth-scroll -
yarn add vue3-smooth-scroll -
pnpm add vue3-smooth-scroll
Imports
- VueSmoothScroll
import { VueSmoothScroll } from 'vue3-smooth-scroll'import VueSmoothScroll from 'vue3-smooth-scroll'
- smoothScroll
import { smoothScroll } from 'vue3-smooth-scroll'const smoothScroll = inject('smoothScroll') - SmoothScrollOptions
import type { SmoothScrollOptions } from 'vue3-smooth-scroll'
Quickstart
import { createApp, ref, inject } from 'vue';
import VueSmoothScroll from 'vue3-smooth-scroll';
const App = {
template: `
<div id="main-content">
<h1>Vue3 Smooth Scroll Example</h1>
<nav>
<a href="#section1" v-smooth-scroll="{ duration: 800, offset: -20 }">Go to Section 1</a> |
<a href="#section2" v-smooth-scroll="{ container: '#scrollContainer', duration: 1200 }">Go to Section 2 (in container)</a> |
<button @click="scrollToMyEl" style="margin-left: 10px;">Scroll to My Element (Programmatic)</button>
</nav>
<div style="height: 500px; background: #f0f0f0; margin-top: 20px; padding: 20px;">
<p>Content before sections to allow sufficient scrolling space.</p>
<p>This area provides initial context before the scroll targets.</p>
</div>
<section id="section1" style="height: 400px; background: lightblue; padding: 20px; border-radius: 8px; margin-top: 40px;">
<h2>Section 1</h2>
<p>This is the first section. We will scroll here using a directive with custom options.</p>
</section>
<div id="scrollContainer" style="height: 300px; overflow-y: scroll; border: 1px solid #ccc; margin-top: 40px; border-radius: 8px;">
<div style="height: 600px; padding: 20px;">
<p>Content inside a custom scroll container.</p>
<p>Scrolling within this container is independent of the main window scroll.</p>
<div style="height: 200px; background: #e0f7fa; margin: 15px 0;">Placeholder</div>
<section id="section2" ref="myContainerEl" style="height: 150px; background: lightcoral; padding: 10px; border-radius: 5px;">
<h3>Section 2 (inside container)</h3>
<p>This section is specifically located inside the custom scrollable div.</p>
</section>
</div>
</div>
<div style="height: 700px; background: #f0f0f0; margin-top: 40px; padding: 20px;">
<p>More content after sections to ensure the ability to scroll back up and down.</p>
<p>This helps in testing the scroll behavior effectively.</p>
</div>
<div ref="myEl" style="height: 100px; background: lightgreen; margin-top: 50px; padding: 20px; border-radius: 8px;">
<h3>My Programmatic Target Element</h3>
<p>This element is targeted programmatically from the button click.</p>
</div>
</div>
`,
setup() {
const myEl = ref(null);
const smoothScroll = inject('smoothScroll');
const scrollToMyEl = () => {
if (smoothScroll && myEl.value) {
smoothScroll({
scrollTo: myEl.value,
duration: 700,
offset: -100,
hash: '#myProgrammaticTarget' // Optional hash update
});
}
};
return {
myEl,
scrollToMyEl
};
}
};
const app = createApp(App);
app.use(VueSmoothScroll, {
duration: 500,
updateHistory: true // Global default for history updates
});
app.mount('#app');