Back To Top
Headless back-to-top button that appears past a scroll threshold and restores focus at the top.
| Package | @vizejs/ui/back-to-top |
| Maturity | stable |
| Own the source | vize lib pull back-to-top |
| Requires | — |
| Aliases | back to top, scroll to top, go to top |
| Covers | Ant Design FloatButton.BackTop, Element Plus Backtop |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { BackToTop } from "@vizejs/ui/back-to-top";
Or copy the source into your project with vize lib pull back-to-top (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- Back-to-top button that appears once a scrollable article passes the threshold and returns focus to it. -->
<script setup lang="ts">
import { useId, useTemplateRef } from "vue";
import { BackToTop } from "@vizejs/ui/back-to-top";
const titleId = useId();
const article = useTemplateRef<HTMLElement>("article");
// The article scrolls on its own, so it needs a fixed block size.
const scrollerSize = { blockSize: "12rem", overflow: "auto" };
const sections = ["Installation", "Configuration", "Components", "Theming", "Testing", "Deploying"];
</script>
<template>
<div>
<section ref="article" :aria-labelledby="titleId" :style="scrollerSize">
<h2 :id="titleId">Getting started</h2>
<p v-for="section in sections" :key="section">
{{ section }}: read this chapter before moving on to the next one.
</p>
</section>
<BackToTop :target="article" :threshold="120" />
</div>
</template>
API
BackToTop
Source: src/families/actions/back-to-top/back-to-top.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
threshold |
number |
400 |
Scroll offset in CSS pixels after which the button becomes visible. |
target |
BackToTopTarget |
null |
Scroll container: an element, a CSS selector, or null for the window. |
behavior |
BackToTopBehavior |
"smooth" |
Scroll animation. "smooth" downgrades to "auto" when the user prefers reduced motion. |
focusTarget |
HTMLElement | string | null |
null |
Element or CSS selector that receives focus after scrolling, so keyboard and screen-reader users land at the top too. null focuses the scroll container element, or leaves focus on the button for the window. |
ariaLabel |
string |
undefined |
Accessible name when the slot does not provide visible text. |
Events
| Event | Payload | Description |
|---|---|---|
click |
[nativeEvent: MouseEvent] |
Fired before scrolling. Call preventDefault() to keep the position. |
scroll-top |
[container: HTMLElement | Window] |
Fired after scrolling to the top was requested. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
BackToTopSlotState |
Button contents. Defaults to "Back to top". |
Exposed
| Member | Type | Description |
|---|---|---|
visible |
boolean |
Whether the container is scrolled past threshold. |
state |
BackToTopState |
Stable state token for styling and tests. |
scrollTop |
number |
Latest observed scroll offset in CSS pixels. |
element |
HTMLButtonElement | null |
Rendered native button. |
scrollToTop |
() => boolean |
Scroll the container to the top and move focus like an activation would. |
refresh |
() => number |
Re-read the scroll offset immediately. |
Behavior
Normative behavior for @vizejs/ui/back-to-top (back-to-top.vue). Every row is proven by
the named test.
| State x input | Observable outcome | Proven by |
|---|---|---|
scroll offset below threshold |
The native button is hidden (out of the tab order and accessibility tree) with data-state="hidden". |
stays hidden until the container passes the threshold |
scroll offset at or past threshold |
The button is shown with data-state="visible". |
stays hidden until the container passes the threshold |
| click | The container scrolls to the top smoothly, focus moves to the container (given tabindex="-1"), and scroll-top fires. |
activation scrolls smoothly to the top and focuses the container |
focusTarget |
Focus moves to that element instead; a focused button stays visible below the threshold until it blurs. | focusTarget receives focus and a focused button stays visible |
prefers-reduced-motion: reduce |
behavior="smooth" is downgraded to an instant jump. |
reduced motion downgrades smooth scrolling and click is preventable |
click handler calls preventDefault() |
No scrolling and no scroll-top. |
reduced motion downgrades smooth scrolling and click is preventable |
target=null |
The window's scrollY drives visibility. |
window scrolling is the default container |
| expose | refresh(), scrollToTop(), visible, state, scrollTop, and element are available. |
expose reads offsets and scrolls on demand |
| SSR | Server markup is byte-identical and hidden; no listeners are attached during render. | renders a hidden, deterministic button on the server |
| hydration | The server button is reused with zero warnings; scroll listening starts after mount. | hydrates without diagnostics and starts listening after mount |
| public types | Behavior, target, and numeric threshold are closed contracts. | src/families/actions/back-to-top/back-to-top.types.test-d.ts |
| DOM/SSR/Vapor | back-to-top.vue compiles in every renderer lane. |
scripts/check-renderers.ts |
| Target | Public contract |
|---|---|
| Root | native button, part="root", data-vize-ui="back-to-top", data-state |
BackToTop ships no stylesheet.