Dealing with scroll position when you insert content is usually a difficult problem to solve. We'll see how to use React life cycle methods to solve it elegantly.

Insertion at the bottom

The first example is to maintain the scroll position at the bottom when an element is inserted at the bottom. A common use case is a chat application.

In order to scroll at the bottom, we can do that on componentDidUpdate. It happens every time the element is re-rendered.

componentDidUpdate: function() {
  var node = this.getDOMNode();
  node.scrollTop = node.scrollHeight;

But this is going to always scroll to the bottom, which can be very annoying if you want to read what was above. Instead you want to scroll only if the user was already at the bottom. To do that, we can check the scroll position before the component has updated with componentWillUpdate and scroll if necessary at componentDidUpdate

componentWillUpdate: function() {
  var node = this.getDOMNode();
  this.shouldScrollBottom = node.scrollTop + node.offsetHeight === node.scrollHeight;
componentDidUpdate: function() {
  if (this.shouldScrollBottom) {
    var node = this.getDOMNode();
    node.scrollTop = node.scrollHeight

Note: we use this.shouldScrollBottom = ...; and not this.setState({shouldScrollBottom: ...}); because we don't want to trigger another render. We just need to manage that value between the two events.

Insertion at the top

The other use case is adding elements at the top of the page but doing so without screwing up the current scroll position of the user. An example is a log view where you can scroll to the top to read historical context.

This is using a similar technique. On componentWillUpdate we store the scroll position and on componentDidUpdate we scroll to the added delta.

componentWillUpdate: function() {
  var node = this.getDOMNode();
  this.scrollHeight = node.scrollHeight;
  this.scrollTop = node.scrollTop;
componentDidUpdate: function() {
  var node = this.getDOMNode();
  node.scrollTop = this.scrollTop + (node.scrollHeight - this.scrollHeight);


React has not been designed to handle scroll position natively. However, it provides escape hatches from the declarative paradigm in order to be able to implement them.

If you liked this article, you might be interested in my Twitter feed as well.
  • James I.

    I especially like your second example. It is really common for sites with real-time streams to insert content above the fold, and it almost never makes sense to shift down the content that the user is currently reading. In the longer-term I think CSS and the browser should implement this behavior for us. I brought it up awhile back on the CSS WG list but it didn't really catch on:

    In the meantime, handling this with React is really nice since all of the componentWillUpdate calls can run before React modifies the DOM, nicely batching together all layout reads before dirtying the layout. Yet another example of how React avoids layout thrash in a practical manner.

  • mhils

    Thanks for the great snippet!
    Small correction: It should be node.clientHeight instead of node.offsetHeight, as the latter one includes border - scrollHeight doesn't.

  • metabrew

    Great examples, thanks!

    Is there a nice way to do something like occlusion culling, so only the dom nodes that would be visible are rendered?

    With fast infinite streams the amount of dom nodes would grow quickly, and would presumably get a bit slow without some sort of management.

  • Thank you! You saved about 3 hours of googling around and scratching my head.

  • Thank you!

    getDOMNode deprecated it should be replaced by React.findDOMNode (this.refs.scrollbar)

  • hitesh

    thank you so much .......I search lot finally got solution

  • Cheton Wu

    In your example of "Insertion at the bottom", it should detect if the horizontal scrollbar becomes visible afterwards. For example:

    componentWillUpdate: function() {
    let hScrollBarHeight = (node.scrollWidth != node.clientWidth) ? 20 : 0;
    this.shouldScrollBottom = ((node.scrollTop + node.clientHeight + hScrollBarHeight) >= node.scrollHeight);

  • Cheton Wu

    Yes! I turned to use node.clientHeight instead of node.offsetHeight since the offsetHeight includes border width.

  • Ruben Martinez Jr.

    This is great! Thanks! I do have one question: do you know of any way to handle inserting image tags, where the image takes some non-zero amount time to fully load and thus reach its final height?
    I've thought of somehow doing something to detect changes in an element's scrollHeight, but other than that I'm unsure.

  • Basarat Ali Syed

    and now `ReactDOM.findDOMNode` 🙂

  • ivan calderon

    were you able to solve this? im having a similar issue

  • ivan calderon

    actually is pretty easy, you just need to set a fixed height for the images or include the images dimensions in your api response so you can inline-style them

  • ivan calderon

    actually is pretty easy, you just need to set a fixed height for the images or include the images dimensions in your api response so you can inline-style them.

  • Now you can get rid of findDOMNode function call all together! For example, this.refs.scrollbar should suffice.

  • Costa Austin

    Has much of the core functionality changed with how this works? I have a form component that I am doing error checking on. on error, I'd like to scroll up to where that error was on the form.

  • Piter Kater

    How you archive scroll upate only with `this.refs.scrollbar` ?

  • ibox

    I really don't know why but I cant make it work... node.scrollTop is always set to 0, just $(window).scrollTop() works for me (meteor 1.3 + React)

  • Cheton Wu
  • Cheton Wu

    Sometimes node.scrollTop may become a floating point number after assignment, you better have to convert it to an integer value using Math.ceil(node.scrollTop) to ensure shouldScrollBottom work as expected.

  • Carlos Pinto

    Hi thanks for the great post. I have a chat app where I'm trying to do exactly what you posted above. I am using the refs method to refer to my messages list container.
    The problem I am running into is that for some reason setting the value of scrollTop does nothing. It actually stays at zero as if it was a non-writtable value.

    Note: this only seems to happen when I make use of -webkit-overflow-scrolling: touch; on the HTML element to allow for nice smooth scrolling on iOS webviews.

    Please see full example below. Thoughts???
    Thanks a lot.

  • Hi, Carlos!
    Did you solve the problem?

  • Carlos Pinto

    I did not. I removed the webkit-overflow-scrolling-touch.

  • John Kaplan

    I had the same problem & finally found the problem. It was that the component react was referencing didn't own the scrollbar - the window did - so inside componentDidUpdate the following line works in a general way to scroll to the bottom:
    window.scrollTop = document.body.scrollHeight;

  • Xiaohui Liu

    great solution! Thanks for sharing


Related Posts

  • September 24, 2011 Javascript: Cyclic Object Detection (11)
    URLON.stringify() suffer from a problem, when passed an object that contains a cycle, it will never stop. This article shows 3 techniques in order to detect if an object is cyclical. Edit the object: Mark In order to detect a cycle in an object, the method we learn at school is to […]
  • September 14, 2011 CSS – One Line Justify (25)
    I came across a CSS problem, text-align: justify does not work with only one line. Justify behavior The reason is because it has been designed with paragraphs in mind. It justifies all the lines but the last one. Normal Justify Lorem ipsum dolor sit amet, consectetur […]
  • January 25, 2017 Anatomy of a JavaScript Pretty Printer (0)
    During the past few weeks, I've been working on prettier, which is a JavaScript pretty printer. We are approaching the phase where we can actually use it so this is a good time to explain how it works. We're going to go through an example if (!pretty) { makePretty() } String […]
  • August 25, 2009 Test – Are you a Javascript Guru? (0)
    Javascript is a very flexible language, I made a compilation of some edge cases that you may have encountered while programming. The main goal is to point out some interesting specific behaviors. Concatenation 1] var result = [10] + 1; [10, 1] "101" 11 Explanation: The Array […]
  • June 8, 2012 CSS – Absolute position taking into account padding (6)
    When looking at the code of I remarked that they are not using top and left in order to position their images but margin-top and margin-left. I've been wondering why for some time and finally found the reason. It is a way to position absolutely elements in a container and […]