Use anchors with react-router
React Router Hash Link worked for me and is easy to install and implement:
$ npm install --save react-router-hash-link
In your component.js import it as Link:
import { HashLink as Link } from 'react-router-hash-link';
And instead of using an anchor <a>
, use <Link>
:
<Link to="home-page#section-three">Section three</Link>
Note: I used HashRouter
instead of Router
:
Here is one solution I have found (October 2016). It is is cross-browser compatible (tested in Internet Explorer, Firefox, Chrome, mobile Safari, and Safari).
You can provide an onUpdate
property to your Router. This is called any time a route updates. This solution uses the onUpdate property to check if there is a DOM element that matches the hash, and then scrolls to it after the route transition is complete.
You must be using browserHistory and not hashHistory.
The answer is by "Rafrax" in Hash links #394.
Add this code to the place where you define <Router>
:
import React from 'react';import { render } from 'react-dom';import { Router, Route, browserHistory } from 'react-router';const routes = ( // your routes);function hashLinkScroll() { const { hash } = window.location; if (hash !== '') { // Push onto callback queue so it runs after the DOM is updated, // this is required when navigating from a different page so that // the element is rendered on the page before trying to getElementById. setTimeout(() => { const id = hash.replace('#', ''); const element = document.getElementById(id); if (element) element.scrollIntoView(); }, 0); }}render( <Router history={browserHistory} routes={routes} onUpdate={hashLinkScroll} />, document.getElementById('root'))
If you are feeling lazy and don't want to copy that code, you can use Anchorate which just defines that function for you. https://github.com/adjohnson916/anchorate
Here's a simple solution that doesn't require any subscriptions nor third-party packages. It should work with react-router@3
and above and react-router-dom
.
Working example: https://fglet.codesandbox.io/
Source (unfortunately, it doesn't currently work within the editor):
#ScrollHandler Hook Example
import { useEffect } from "react";import PropTypes from "prop-types";import { withRouter } from "react-router-dom";const ScrollHandler = ({ location, children }) => { useEffect( () => { const element = document.getElementById(location.hash.replace("#", "")); setTimeout(() => { window.scrollTo({ behavior: element ? "smooth" : "auto", top: element ? element.offsetTop : 0 }); }, 100); }, [location]); ); return children;};ScrollHandler.propTypes = { children: PropTypes.node.isRequired, location: PropTypes.shape({ hash: PropTypes.string, }).isRequired};export default withRouter(ScrollHandler);
#ScrollHandler Class Example
import { PureComponent } from "react";import PropTypes from "prop-types";import { withRouter } from "react-router-dom";class ScrollHandler extends PureComponent { componentDidMount = () => this.handleScroll(); componentDidUpdate = prevProps => { const { location: { pathname, hash } } = this.props; if ( pathname !== prevProps.location.pathname || hash !== prevProps.location.hash ) { this.handleScroll(); } }; handleScroll = () => { const { location: { hash } } = this.props; const element = document.getElementById(hash.replace("#", "")); setTimeout(() => { window.scrollTo({ behavior: element ? "smooth" : "auto", top: element ? element.offsetTop : 0 }); }, 100); }; render = () => this.props.children;};ScrollHandler.propTypes = { children: PropTypes.node.isRequired, location: PropTypes.shape({ hash: PropTypes.string, pathname: PropTypes.string, })};export default withRouter(ScrollHandler);