React component to keep content always scrolled down
npm install react-stay-scrolled[![Build Status][build-badge]][build]
[![npm package][npm-badge]][npm]
[![Coverage Status][coveralls-badge]][coveralls]
> Keep your component, such as message boxes, scrolled down
You can see the simplest demo here: Live demo
``bash`
$ npm install --save react-stay-scrolled
Run examples:
`bash`
$ npm ci
$ cd examples
$ npm ci
$ npm start
`javascript
import { useRef, useLayoutEffect } from 'react';
import PropTypes from 'prop-types';
import useStayScrolled from 'react-stay-scrolled';
const Messages = ({ messages }) => {
const listRef = useRef();
const { stayScrolled/, scrollBottom/ } = useStayScrolled(listRef);
// Typically you will want to use stayScrolled or scrollBottom inside
// useLayoutEffect, because it measures and changes DOM attributes (scrollTop) directly
useLayoutEffect(() => {
stayScrolled();
}, [messages.length])
return (
Messages.propTypes = {
messages = PropTypes.array,
}
`
Another use case is notifying users when there is a new message down the window that they haven't read:
`javascript
// messages.jsx
import { useState, useRef, useCallback, useLayoutEffect } from 'react';
import PropTypes from 'prop-types';
import useStayScrolled from 'react-stay-scrolled';
import Message from './message.jsx';
const Messages = ({ messages }) => {
const [notifyNewMessage, setNotifyNewMessage] = useState(false);
const ref = useRef();
const { stayScrolled, isScrolled } = useStayScrolled(ref);
// The element just scrolled down - remove new messages notification, if any
const onScroll = useCallback(() => {
if (isScrolled()) setNotifyNewMessage(false);
}, []);
useLayoutEffect(() => {
// Tell the user to scroll down to see the newest messages if the element wasn't scrolled down
setNotifyNewMessage(!stayScrolled());
}, [messages.length])
return (
You can use react-spring to animate the scroll:
`javascript
import { useRef, useCallback, useLayoutEffect } from 'react';
import useStayScrolled from 'react-stay-scrolled';
import { useSpring, animated } from '@react-spring/web';const SpringStayScrolled = ({
provideControllers,
onScroll,
getRunScroll,
}) => {
const ref = useRef(null);
const [{ scrollTop }, animateScroll] = useSpring(() => ({ scrollTop: 0 }), []);
const runScroll = useCallback(offset => animateScroll.start({
scrollTop: offset,
from: { scrollTop: ref.current ? ref.current.scrollTop : 0 },
}), [animateScroll])
const { scrollBottom } = useStayScrolled(ref, { runScroll });
useLayoutEffect(() => { scrollBottom(); }, []);
return (
{/ ... /}
);
};
`Arguments
$3
Type: a React
ref, requiredA
ref to the DOM element whose scroll position you want to control$3
Type:
object, default:`javascript
{
initialScroll: null,
inaccuracy: 0,
runScroll: defaultRunScroll,
}
`$3
Type:
number, default: nullIf provided, the scrolling element will mount scrolled with the provided value. If
Infinity is provided, it will mount scrolled to the bottom.$3
Type:
number, default: 0Defines an error margin, in pixels, under which
stayScrolled will still scroll to the bottom$3
Type:
function: (offset) => undefined, default: (offset) => { ref.current.scrollTop = offset; } where ref is the first valueUsed for animating dom scrolling. You can use dynamic.js, Velocity, jQuery, or your favorite animation library. Here are examples of possible, tested
runScroll values:`js
const easing = 'linear';
const duration = 100;const dynamicsRunScroll = (domRef) => (offset) => {
dynamics.animate(domRef.current, {
scrollTop: offset,
}, {
type: dynamics[easing],
duration,
});
};
const jqueryRunScroll = (domRef) => (offset) => {
jQuery(domRef.current).animate({ scrollTop: offset }, duration, easing);
};
const velocityRunScroll = (domRef) => (offset) => {
Velocity(
domRef.current.firstChild,
'scroll',
{
container: domRef.current,
easing,
duration,
offset,
}
);
};
`Return value
Type:
object, shape: { stayScrolled, scrollBottom, scroll, isScrolled }Four functions used for controlling scroll behavior.
$3
Type:
function: () => boolScrolls down the element if it was already scrolled down - useful for when a user is reading previous messages, and you don't want to interrupt. Returns true if it performed a scrolled down, false otherwise.
$3
Type:
function: (position: Integer) => voidScrolls down to the desired position. If given
Infinity, it scrolls to the bottom$3
Type:
function: () => voidScrolls down the wrapper element, regardless of current position. Equivalent to
() => scroll(Infinity).$3
Type:
function: () => bool`Returns true if the dom element is scrolled all the way down (within the inaccuracy provided).
See the LICENSE file for license rights and limitations (MIT).
[build-badge]: https://img.shields.io/github/actions/workflow/status/dotcore64/react-stay-scrolled/test.yml?event=push&style=flat-square
[build]: https://github.com/dotcore64/react-stay-scrolled/actions
[npm-badge]: https://img.shields.io/npm/v/react-stay-scrolled.svg?style=flat-square
[npm]: https://www.npmjs.org/package/react-stay-scrolled
[coveralls-badge]: https://img.shields.io/coveralls/dotcore64/react-stay-scrolled/master.svg?style=flat-square
[coveralls]: https://coveralls.io/r/dotcore64/react-stay-scrolled