BackToTop
A button that appears once a page is scrolled down, and takes the reader and their focus back to the top.
On this page
BackToTopExample.tsx
import { BackToTop } from "@/components/BackToTop";
export function BackToTopExample() {
return <BackToTop placement="inline" visible showLabel />;
}Examples
Variants
With its label
Announced as: “Back to top, button”
Sizes
On every page
The site shell shows one in the bottom-right corner of every page, once it's scrolled down by one screen, on screens at least 768px wide. Scroll down to see this site's own.
Customized with component tokens
CSS
.customBackToTop {
--component-back-to-top-offset: var(--space-layout-sm);
--component-back-to-top-shadow: var(--effect-elevation-raised);
}Props
Any native prop not listed here goes to the root element unchanged.
There are no children: the arrow and the label are built in. It's a library Button, so the native button props, className and ref reach the button, except disabled and aria-pressed. An onClick runs first; calling event.preventDefault() in it cancels the scroll and the focus move.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "primary" | "secondary" | "ghost" | "secondary" | Emphasis: one primary action per view, secondary for the rest, ghost for toolbars. |
size | "sm" | "md" | "md" | 32px or 40px tall. |
label | string | "Back to top" | The accessible name, and the visible text with showLabel. |
showLabel | boolean | false | Shows the label beside the arrow. Without it the button is icon-only and shows its name as a tooltip on hover and focus. |
threshold | number | the viewport's height | How far, in pixels, the page scrolls before the button appears. In the corner it only appears on viewports at least 48em (768px) wide; phones scroll to the top from the status bar. |
visible | boolean | — | Overrides the automatic visibility: true always shows it, false always hides it. Hidden, it's out of the tab order and the accessibility tree. |
placement | "corner" | "inline" | "corner" | corner is fixed at the viewport's bottom-right corner (bottom-left in right-to-left text), with a shadow. inline sits in the flow. |
focusTarget | () => HTMLElement | null | main's h1, else main | Where focus goes after scrolling. An element that can't take focus gets tabindex=-1 first. |
Component tokens
| Token | Copy | Description | Aliases | Light | Dark | Preview |
|---|---|---|---|---|---|---|
--component-back-to-top-offset | Distance of the corner button from the viewport's block-end and inline-end edges. | space.layout.xs | 1remfrom space.4 | 1remfrom space.4 | ||
--component-back-to-top-shift | How far it slides in when it appears (none under reduced motion). | space.stack.sm | 0.5remfrom space.2 | 0.5remfrom space.2 | ||
--component-back-to-top-shadow | Shadow of the corner button, so it reads above the page. The inline placement has none. | effect.elevation.overlay | 0px 4px 12px 0px rgb(0% 0% 0% / 0.12)from effect.shadow.md | 0px 4px 12px 0px rgb(0% 0% 0% / 0.4)from effect.shadow.md-dark | ||
--component-back-to-top-layer | Stacking order of the corner button: the same level as the sticky header. | layer.sticky | 2from layer.2 | 2from layer.2 | No preview | |
--component-back-to-top-duration | Duration of showing and hiding (0ms under reduced motion). | motion.duration.base | 250msfrom motion.time.250Reduced motion: 0ms | 250msfrom motion.time.250 | No preview |
Accessibility
- A native button named "Back to top" (or its label). Icon-only, it shows that name as a tooltip on hover and keyboard focus; the arrow is decorative.
- In the corner it only appears on viewports at least 48em (768px) wide: phones scroll to the top from the status bar, and on a small screen with large text the sticky header leaves no room to keep focused content clear of it.
- Until the page is scrolled past the threshold it's hidden from sight, from the tab order and from the accessibility tree. Once shown, it stays while it has focus, so focus never drops to the page when it scrolls up under the button.
- Activating it scrolls to the top and moves focus to the page's h1 (or main), without a second scroll, so the next Tab continues from the top of the page.
- It scrolls smoothly only when the reader hasn't asked for reduced motion; otherwise it jumps. Showing and hiding fade and slide in, with no slide and no fade time under reduced motion.
- While the corner button shows, the page keeps a scroll padding at its bottom as tall as the button and its offset, so it never covers a focused element (WCAG 2.4.11).
- It has Button's focus ring, 40px target (32px when small), contrast and forced-colors look.
| Key | Action |
|---|---|
| Enter | Scrolls to the top and moves focus to the page's heading. |
| Space | Scrolls to the top and moves focus to the page's heading. |