BackToTop
A portal-rendered floating button that reveals after the user scrolls past a threshold and returns to the top of the page when clicked.
Renders an IconButton with an accessible label; toggles a data attribute for visibility rather than removing from the tree.
Preview
tsx
import { BackToTop } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<div style={{ minHeight: '200vh', padding: '1rem' }}>
<p>Scroll down to reveal the floating back-to-top button.</p>
<BackToTop />
</div>
);
}
import { BackToTop } from "@arshad-shah/cynosure-react";
<BackToTop showAfter={300} position="bottom-right" />The button is rendered through a Portal so it floats above the rest of the page; pass disablePortal to render it inline (useful in tests).
Variants
Section titled “Variants”Threshold
Section titled “Threshold”showAfter controls how many pixels the page must scroll before the button appears. The default is 300.
Preview
tsx
import { BackToTop } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<div style={{ minHeight: '150vh', padding: '1rem' }}>
<p>Scroll just 100px to reveal the button.</p>
<BackToTop showAfter={100} />
</div>
);
}
Position
Section titled “Position”Use position to anchor the button to one of three corners along the bottom edge.
Preview
tsx
import { BackToTop } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<div style={{ minHeight: '200vh', padding: '1rem' }}>
<p>The button is anchored to the bottom-left corner.</p>
<BackToTop position="bottom-left" />
</div>
);
}
Custom icon
Section titled “Custom icon”Replace the default chevron with any ReactNode via the icon prop.
Preview
tsx
import { BackToTop } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<div style={{ minHeight: '200vh', padding: '1rem' }}>
<p>Scroll to reveal a custom-iconed back-to-top button.</p>
<BackToTop
icon={
<svg
aria-hidden="true"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M12 19V5" />
<path d="M5 12l7-7 7 7" />
</svg>
}
label="Scroll to top"
/>
</div>
);
}
Inline (no portal)
Section titled “Inline (no portal)”Pass disablePortal to render the button at its current position in the tree.
Preview
tsx
import { BackToTop } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem' }}>
<span style={{ fontSize: '0.875rem', color: 'var(--cynosure-color-fg-muted)' }}>
Rendered inline (no portal):
</span>
<BackToTop disablePortal showAfter={0} />
</div>
);
}
PropTypeDefaultDescription
showAfter
number
300
Scroll distance (in px) past which the button becomes visible.
position
"bottom-right"|"bottom-left"|"bottom-center"
bottom-right
One of the corner / center positioning presets.
smooth
boolean
true
Use smooth scrolling on click. Forced to `"auto"` under
`prefers-reduced-motion: reduce`.
container
Element|DocumentFragment|(() => Element|null)
—
Portal target. Defaults to `document.body`.
disablePortal
boolean
—
Render inline (skip portal). Useful in tests or when the parent scroll
container isn't `document`.
icon
ReactNode
—
Replace the default chevron-up icon.
label
string
Back to top
Accessible label for the icon-only button.
Accessibility
Section titled “Accessibility”- The underlying element is a
<button>rendered via Cynosure’sIconButton; the icon is hidden from assistive tech. label(default"Back to top") becomes the button’s accessible name — override for localisation.- A
data-visibleattribute reflects the visible state so consumers can style the show/hide transition without re-mounting. - When
prefers-reduced-motion: reduceis set,smoothis ignored and the page jumps instantly. - The button remains tab-focusable while hidden, but
data-visible="false"lets CSS hide it from sight.
Recipes
Section titled “Recipes”- Mount once at the root of your layout — it positions itself fixed via the
positionpreset. - Lower
showAfter(e.g.120) on shorter pages so the affordance appears sooner. - Set
disablePortalinside unit tests wheredocument.bodydoesn’t exist yet. - Use
containerto portal into a scroll container other thandocument.bodyfor scoped layouts.