9

I am pretty much familiar with the React.js but new to Gatsby.

I want to detect the previous page URL in Gatsby?

Soroush Chehresa
  • 5,490
  • 1
  • 14
  • 29
Profer
  • 553
  • 8
  • 40
  • 81
  • If it uses browser's history, the prev url should be saved in `document.referrer` I think. – extempl Mar 01 '19 at 10:28
  • Nopes it is blank. I am not getting anything inside it. – Profer Mar 01 '19 at 10:34
  • Possible duplicate of [How to get the previous page URL using JavaScript?](https://stackoverflow.com/questions/5788108/how-to-get-the-previous-page-url-using-javascript) – Soroush Chehresa Mar 01 '19 at 10:58
  • @soroushchehresa No this is not a duplicate. Please refer the question. I have to do with gatsby and event I didn't put the javascript tag. And also gatsby build will not execute the window variable. So please take back your close vote. – Profer Mar 01 '19 at 11:01

4 Answers4

18

You can pass down state using the Link component:

import React from 'react';
import { Link } from 'gatsby';

const PrevPage = () => (
  <div>
    <Link
      to={`/nextpage`}
      state={{ prevPath: location.pathname }}
    >
      Next Page
    </Link>
  </div>
)

const NextPage = (props) => (
  <div>
    <p>previous path is: {props.location.state.prevPath}</p>
  </div>
);

Then you have access to prevPath from this.props.location.state in the next page.

Soroush Chehresa
  • 5,490
  • 1
  • 14
  • 29
  • Thank you!!! There is no way to do this with gatsby and hence I will go with your above answer – Profer Mar 02 '19 at 05:57
  • @Profer Don't mention it ;) – Soroush Chehresa Mar 02 '19 at 13:32
  • 1
    @Profer Why do you say there's no way to do this with Gatsby? This answer shows you how to do it with Gatsby's Link component (which builds on the Reach Router Link component). – coreyward Mar 02 '19 at 21:20
  • @coreyward because the above answer is just a trick. If there is a way then Gatsby itself provide some prop or constant. – Profer Mar 03 '19 at 10:36
  • 1
    @Profer How is it a trick? Gatsby itself provides the `state` prop on `Link` and provides the value passed to it to the page component of the following page. [It's documented here.](https://www.gatsbyjs.org/docs/gatsby-link/#pass-state-as-props-to-the-linked-page). – coreyward Mar 03 '19 at 21:47
  • @coreyward is correct. Passing state into the Link component is a Gatsby Link feature, which is extended from reach/router's api. – Vien Tang Mar 10 '19 at 21:49
  • This is wrong.. location.state persists.. so when you go to this page you will have that state set correctly but then if you navigate to another page and do a browser back the state will have this same value which is false. – itdoesntwork Oct 23 '19 at 13:33
12

Full credit to @soroushchehresa's answer — this answer is just extras built upon it.

Gatsby will throw error during production build, since location is not available during server-side rendering. You could get around it by checking for window object first:

class Page extends React.Component {
  state = {
    currentUrl: '',
  }

  componentDidMount() {
    if (typeof window == 'undefined') return
    this.setState({ currentUrl: window.location.href })
  }

  render() {
    return (
      <Link to="..." state={{ prevUrl: this.state.currentUrl }}>
    )
  }
}

But this requires us to implement this on every page, which is tedious. Gatsby has already set up @reach/router for server-side rendering, so we can hook into its location props. Only router components get that props, but we can use @reach/router's Location component to pass it to other components.

With that, we can write a custom Link component that always pass previous url in its state:

// ./src/components/link-with-prev-url.js

import React from 'react'
import { Location } from '@reach/router'
import { Link } from 'gatsby'

const LinkWithPrevUrl = ({ children, state, ...rest }) => (
  <Location>
    {({ location }) => (
                      //make sure user's state is not overwritten
      <Link {...rest} state={{ prevUrl: location.href, ...state}}>
        { children }
      </Link>
    )}
  </Location>
)

export { LinkWithPrevUrl as Link }

Then we can import our custom Link component instead of Gatsby's Link:

-  import { Link } from 'gatsby'
+  import { Link } from './link-with-prev-url'

Now each Gatsby page component will get this previous url props:

const SomePage = ({ location }) => (
  <div>previous path is {location.state.prevUrl}</div>
);

You might also consider creating a container that store state for the client side & use the wrapRootElement or wrapPageElement in both gatsby-ssr.js and gatsby-browser.js.

Derek Nguyen
  • 11,294
  • 1
  • 40
  • 64
  • 2
    Slick. I like it. – coreyward Mar 03 '19 at 21:48
  • 2
    I came across the initial answer by way of the Gatsby docs, actually, and was hoping to avoid the tedium. Thanks for expounding on it to create a streamlined approach—great idea! I was looking at the `onRouteUpdate` API in gatsby-browser.js which exposes `location, prevLocation` but, as far as I can tell, it only makes them available for use outside the scope of my Gatsby/React components. – Victor Mar 04 '19 at 16:21
10

These answers are partially correct. If you set state using link api then the state persists in browser history.

So if you go from Page1 to Page2 then the eg the state.prevUrl will correctly be set to Page1

But if the you go to Page3 from Page2 and then do a browser back then the state.prevUrl will still be Page1 which is false.

Best way I found to deal with this is to add something like this on the gatsby-browser.js

export const onRouteUpdate = ({ location, prevLocation }) => {
  if (location && location.state)
    location.state.referrer = prevLocation ? prevLocation.pathname : null
}

this way you will have always the previous url available on location.

itdoesntwork
  • 1,745
  • 16
  • 31
2

I resolved my problem with the below piece of code. Here is the ref link https://github.com/gatsbyjs/gatsby/issues/10410

// gatsby-browser.js
exports.onRouteUpdate = () => {
  window.locations = window.locations || [document.referrer]
  locations.push(window.location.href)
  window.previousPath = locations[locations.length - 2]
}

Now you can get previousPath can be accessed from anywhere.

Yash Vekaria
  • 2,285
  • 24
  • 24