281
votes

I have a chat widget that pulls up an array of messages every time I scroll up. The problem I am facing now is the slider stays fixed at the top when messages load. I want it to focus on the last index element from the previous array. I figured out that I can make dynamic refs by passing index, but I would also need to know what kind of scroll function to use to achieve that

 handleScrollToElement(event) {
    const tesNode = ReactDOM.findDOMNode(this.refs.test)
    if (some_logic){
      //scroll to testNode      
    }
  }

  render() {

    return (
      <div>
        <div ref="test"></div>
      </div>)
  }
25

25 Answers

507
votes

React 16.8 +, Functional component

const ScrollDemo = () => {
   const myRef = useRef(null)

   const executeScroll = () => myRef.current.scrollIntoView()    
   // run this function from an event handler or an effect to execute scroll 

   return (
      <> 
         <div ref={myRef}>Element to scroll to</div> 
         <button onClick={executeScroll}> Click to scroll </button> 
      </>
   )
}

Click here for a full demo on StackBlits

React 16.3 +, Class component

class ReadyToScroll extends Component {
    constructor(props) {
        super(props)
        this.myRef = React.createRef()  
    }

    render() {
        return <div ref={this.myRef}>Element to scroll to</div> 
    }  

    executeScroll = () => this.myRef.current.scrollIntoView()
    // run this method to execute scrolling. 
}

Class component - Ref callback

class ReadyToScroll extends Component {  
    render() {
        return <div ref={ (ref) => this.myRef=ref }>Element to scroll to</div>
    } 

    executeScroll = () => this.myRef.scrollIntoView()
    // run this method to execute scrolling. 
}

Don't use String refs.

String refs harm performance, aren't composable, and are on their way out (Aug 2018).

string refs have some issues, are considered legacy, and are likely to be removed in one of the future releases. [Official React documentation]

resource1resource2

Optional: Smoothe scroll animation

/* css */
html {
    scroll-behavior: smooth;
}

Passing ref to a child

We want the ref to be attached to a dom element, not to a react component. So when passing it to a child component we can't name the prop ref.

const MyComponent = () => {
    const myRef = useRef(null)
    return <ChildComp refProp={myRef}></ChildComp>
} 

Then attach the ref prop to a dom element.

const ChildComp = (props) => {
    return <div ref={props.refProp} />
}
92
votes

this worked for me

this.anyRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' })

EDIT: I wanted to expand on this based on the comments.

const scrollTo = (ref) => {
  if (ref && ref.current /* + other conditions */) {
    ref.current.scrollIntoView({ behavior: 'smooth', block: 'start' })
  }
}

<div ref={scrollTo}>Item</div>
41
votes

Just find the top position of the element you've already determined https://www.w3schools.com/Jsref/prop_element_offsettop.asp then scroll to this position via scrollTo method https://www.w3schools.com/Jsref/met_win_scrollto.asp

Something like this should work:

handleScrollToElement(event) {
  const tesNode = ReactDOM.findDOMNode(this.refs.test)
  if (some_logic){
    window.scrollTo(0, tesNode.offsetTop);
  }
}

render() {

  return (
    <div>
      <div ref="test"></div>
    </div>)
}

UPDATE:

since React v16.3 the React.createRef() is preferred

constructor(props) {
  super(props);
  this.myRef = React.createRef();
}

handleScrollToElement(event) {
  if (<some_logic>){
    window.scrollTo(0, this.myRef.current.offsetTop);
  }
}

render() {

  return (
    <div>
      <div ref={this.myRef}></div>
    </div>)
}
16
votes

You can now use useRef from react hook API

https://reactjs.org/docs/hooks-reference.html#useref

declaration

let myRef = useRef()

component

<div ref={myRef}>My Component</div>

Use

window.scrollTo({ behavior: 'smooth', top: myRef.current.offsetTop })
14
votes

Using findDOMNode is going to be deprecated eventually.

The preferred method is to use callback refs.

github eslint

13
votes

Jul 2019 - Dedicated hook/function

A dedicated hook/function can hide implementation details, and provides a simple API to your components.

React 16.8 + Functional Component

const useScroll = () => {
  const elRef = useRef(null);
  const executeScroll = () => elRef.current.scrollIntoView();

  return [executeScroll, elRef];
};

Use it in any functional component.

const ScrollDemo = () => {
    const [executeScroll, elRef] = useScroll()
    useEffect(executeScroll, []) // Runs after component mounts
    
    return <div ref={elRef}>Element to scroll to</div> 
}

full demo

React 16.3 + class Component

const utilizeScroll = () => {
  const elRef = React.createRef();
  const executeScroll = () => elRef.current.scrollIntoView();

  return { executeScroll, elRef };
};

Use it in any class component.

class ScrollDemo extends Component {
  constructor(props) {
    super(props);
    this.elScroll = utilizeScroll();
  }

  componentDidMount() {
    this.elScroll.executeScroll();
  }

  render(){
    return <div ref={this.elScroll.elRef}>Element to scroll to</div> 
  }
} 

Full demo

10
votes

You can also use scrollIntoView method to scroll to a given element.

handleScrollToElement(event) {
const tesNode = ReactDOM.findDOMNode(this.refs.test)
 if (some_logic){
  tesNode.scrollIntoView();
  }
 }

 render() {
  return (
   <div>
     <div ref="test"></div>
   </div>)
}
10
votes

I might be late to the party but I was trying to implement dynamic refs to my project the proper way and all the answer I have found until know aren't quiet satisfying to my liking, so I came up with a solution that I think is simple and uses the native and recommended way of react to create the ref.

sometimes you find that the way documentation is wrote assumes that you have a known amount of views and in most cases this number is unknown so you need a way to solve the problem in this case, create dynamic refs to the unknown number of views you need to show in the class

so the most simple solution i could think of and worked flawlessly was to do as follows

class YourClass extends component {

state={
 foo:"bar",
 dynamicViews:[],
 myData:[] //get some data from the web
}

inputRef = React.createRef()

componentDidMount(){
  this.createViews()
}


createViews = ()=>{
const trs=[]
for (let i = 1; i < this.state.myData.lenght; i++) {

let ref =`myrefRow ${i}`

this[ref]= React.createRef()

  const row = (
  <tr ref={this[ref]}>
<td>
  `myRow ${i}`
</td>
</tr>
)
trs.push(row)

}
this.setState({dynamicViews:trs})
}

clickHandler = ()=>{

//const scrollToView = this.inputRef.current.value
//That to select the value of the inputbox bt for demostrate the //example

value=`myrefRow ${30}`

  this[value].current.scrollIntoView({ behavior: "smooth", block: "start" });
}


render(){

return(
<div style={{display:"flex", flexDirection:"column"}}>
<Button onClick={this.clickHandler}> Search</Button>
<input ref={this.inputRef}/>
<table>
<tbody>
{this.state.dynamicViews}
<tbody>
<table>
</div>


)

}

}

export default YourClass

that way the scroll will go to whatever row you are looking for..

cheers and hope it helps others

10
votes

I had a simple scenario, When user clicks on the menu item in my Material UI Navbar I want to scroll them down to the section on the page. I could use refs and thread them through all the components but I hate threading props props multiple components because that makes code fragile.

I just used vanilla JS in my react component, turns out it works just fine. Placed an ID on the element I wanted to scroll to and in my header component I just did this.

const scroll = () => {
  const section = document.querySelector( '#contact-us' );
  section.scrollIntoView( { behavior: 'smooth', block: 'start' } );
};
10
votes

The nicest way is to use element.scrollIntoView({ behavior: 'smooth' }). This scrolls the element into view with a nice animation.

When you combine it with React's useRef(), it can be done the following way.

import React, { useRef } from 'react'

const Article = () => {
  const titleRef = useRef()

  function handleBackClick() {
      titleRef.current.scrollIntoView({ behavior: 'smooth' })
  }

  return (
      <article>
            <h1 ref={titleRef}>
                A React article for Latin readers
            </h1>

            // Rest of the article's content...

            <button onClick={handleBackClick}>
                Back to the top
            </button>
        </article>
    )
}

When you would like to scroll to a React component, you need to forward the ref to the rendered element. This article will dive deeper into the problem.

7
votes

You could try this way:

 handleScrollToElement = e => {
    const elementTop = this.gate.offsetTop;
    window.scrollTo(0, elementTop);
 };

 render(){
  return(
      <h2 ref={elem => (this.gate = elem)}>Payment gate</h2>
 )}
5
votes

You can use something like componentDidUpdate

componentDidUpdate() {
  var elem = testNode //your ref to the element say testNode in your case; 
  elem.scrollTop = elem.scrollHeight;
};
4
votes

Here is the Class Component code snippet you can use to solve this problem:

This approach used the ref and also scrolls smoothly to the target ref

import React, { Component } from 'react'

export default class Untitled extends Component {
  constructor(props) {
    super(props)
    this.howItWorks = React.createRef() 
  }

  scrollTohowItWorks = () =>  window.scroll({
    top: this.howItWorks.current.offsetTop,
    left: 0,
    behavior: 'smooth'
  });

  render() {
    return (
      <div>
       <button onClick={() => this.scrollTohowItWorks()}>How it works</button>
       <hr/>
       <div className="content" ref={this.howItWorks}>
         Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nesciunt placeat magnam accusantium aliquid tenetur aspernatur nobis molestias quam. Magnam libero expedita aspernatur commodi quam provident obcaecati ratione asperiores, exercitationem voluptatum!
       </div>
      </div>
    )
  }
}
4
votes

I used this inside a onclick function to scroll smoothly to a div where its id is "step2Div".

let offset = 100;
window.scrollTo({
    behavior: "smooth",
    top:
    document.getElementById("step2Div").getBoundingClientRect().top -
    document.body.getBoundingClientRect().top -
    offset
});
3
votes

Follow these steps:

1) Install:

npm install react-scroll-to --save

2) Import the package:

import { ScrollTo } from "react-scroll-to";

3) Usage:

class doc extends Component {
  render() {
    return(
      <ScrollTo>
        {({ scroll }) => (
          <a onClick={() => scroll({ x: 20, y: 500, , smooth: true })}>Scroll to Bottom</a>
        )}
      </ScrollTo>
    )
  }
}
3
votes

After reading through manny forums found a really easy solution.

I use redux-form. Urgo mapped redux-from fieldToClass. Upon error I navigate to the first error on the list of syncErrors.

No refs and no third party modules. Just simple querySelector & scrollIntoView

handleToScroll = (field) => {

    const fieldToClass = {
        'vehicleIdentifier': 'VehicleIdentifier',
        'locationTags': 'LocationTags',
        'photos': 'dropzoneContainer',
        'description': 'DescriptionInput',
        'clientId': 'clientId',
        'driverLanguage': 'driverLanguage',
        'deliveryName': 'deliveryName',
        'deliveryPhone': 'deliveryPhone',
        "deliveryEmail": 'deliveryEmail',
        "pickupAndReturn": "PickupAndReturn",
        "payInCash": "payInCash",
    }

document?.querySelector(`.${fieldToClasses[field]}`)
         .scrollIntoView({ behavior: "smooth" })

}
2
votes

What worked for me:

class MyComponent extends Component {
    constructor(props) {
        super(props);
        this.myRef = React.createRef(); // Create a ref    
    }

    // Scroll to ref function
    scrollToMyRef = () => {
        window.scrollTo({
            top:this.myRef.offsetTop, 
            // behavior: "smooth" // optional
        });
    };

    // On component mount, scroll to ref
    componentDidMount() {
        this.scrollToMyRef();
    }

    // Render method. Note, that `div` element got `ref`.
    render() {
        return (
            <div ref={this.myRef}>My component</div>
        )
    }
}
2
votes

If you want to do it on page load you can use useLayoutEffect, and useRef.

import React, { useRef, useLayoutEffect } from 'react'

const ScrollDemo = () => {

   const myRef = useRef(null)

   useLayoutEffect(() => {
      window.scrollTo({
        behavior: "smooth",
        top: myRef.current.offsetTop,
      });
    }, [myRef.current]);

   return (
      <> 
         <div ref={myRef}>I wanna be seen</div>
      </>
   )
}
2
votes

In order to automatically scroll into the particular element, first need to select the element using document.getElementById and then we need to scroll using scrollIntoView(). Please refer the below code.

   scrollToElement= async ()=>{
      document.getElementById('id001').scrollIntoView();
    } 

The above approach worked for me.

2
votes

If anyone is using Typescript, here is Ben Carp's answer for it:

import React, {useRef} from "react";

export const useScroll = <T extends HTMLElement>(options?: boolean | ScrollIntoViewOptions): [() => void, React.RefObject<T>] => {
    const elRef = useRef<T>(null);
    const executeScroll = () => {
        if (elRef.current) {
            elRef.current.scrollIntoView(options);
        }
    };

    return [executeScroll, elRef];
};
1
votes

Just a heads up, I couldn't get these solutions to work on Material UI components. Looks like they don't have the current property.

I just added an empty div amongst my components and set the ref prop on that.

1
votes

Here is my solution:

I put an invisible div inside main div and made its position absolute. Then set the top value to -(header height) and set the ref on this div. Or you can just react that div with children method.

It's working great so far!

<div className="position-relative">
        <div style={{position:"absolute", top:"-80px", opacity:0, pointerEvents:'none'}}  ref={ref}></div>
0
votes
 <div onScrollCapture={() => this._onScrollEvent()}></div>

 _onScrollEvent = (e)=>{
     const top = e.nativeEvent.target.scrollTop;
     console.log(top); 
}
0
votes

To anyone else reading this who didn't have much luck with the above solutions or just wants a simple drop-in solution, this package worked for me: https://www.npmjs.com/package/react-anchor-link-smooth-scroll. Happy Hacking!

0
votes

This solution works for me in ReactJS

In header.js

function scrollToTestDiv(){
      const divElement = document.getElementById('test');
      divElement.scrollIntoView({ behavior: 'smooth' });
    }

<a class="nav-link" onClick={scrollToTestDiv}> Click here! </a>

In index.html

<div id="test"></div>