This seemingly simple task had me scratching my head for a few hours while I was working on my website. As it turns out, getting the current page URL in Gatsby is not as straightforward as you may think, but also not so complicated to understand.
Let’s look at a few methods of making it happen. But first, you might be wondering why on earth we’d even want to do something like this.
Why you might need the current URL
So before we get into the how, let’s first answer the bigger question: Why would you want to get the URL of the current page? I can offer a few use cases.
Meta tags
The first obvious thing that you’d want the current URL for is meta tags in the document head:
<link rel="canonical" href={url} />
<meta property="og:url" content={url} />
Social Sharing
I’ve seen it on multiple websites where a link to the current page is displayed next to sharing buttons. Something like this (found on Creative Market)
Styling
This one is less obvious but I’ve used it a few times with styled-components. You can render different styles based on certain conditions. One of those conditions can be a page path (i.e. part of the URL after the name of the site). Here’s a quick example:
import React from 'react';
import styled from 'styled-components';
const Layout = ({ path, children }) => (
<StyledLayout path={path}>
{children}
</StyledLayout>
);
const StyledLayout = styled.main`
background-color: ${({ path }) => (path === '/' ? '#fff' : '#000')};
`;
export default Layout;
Here, I’ve created a styled Layout
component that, based on the path, has a different background color.
This list of examples only illustrates the idea and is by no means comprehensive. I’m sure there are more cases where you might want to get the current page URL. So how do we get it?
Understand build time vs. runtime
Not so fast! Before we get to the actual methods and code snippets, I’d like to make one last stop and briefly explain a few core concepts of Gatsby.
The first thing that we need to understand is that Gatsby, among many other things, is a static site generator. That means it creates static files (that are usually HTML and JavaScript). There is no server and no database on the production website. All pieces of information (including the current page URL) must be pulled from other sources or generated during build time or runtime before inserting it into the markup.
That leads us to the second important concept we need to understand: Build time vs. runtime. I encourage you to read the official Gatsby documentation about it, but here’s my interpretation.
Runtime is when one of the static pages is opened in the browser. In that case, the page has access to all the wonderful browser APIs, including the Window API that, among many other things, contains the current page URL.
One thing that is easy to confuse, especially when starting out with Gatsby, is that running gatsby develop
in the terminal in development mode spins up the browser for you. That means all references to the window object work and don’t trigger any errors.
Build time happens when you are done developing and tell Gatsby to generate final optimized assets using the gatsby build
command. During build time, the browser doesn’t exist. This means you can’t use the window object.
Here comes the a-ha! moment. If builds are isolated from the browser, and there is no server or database where we can get the URL, how is Gatsby supposed to know what domain name is being used? That’s the thing — it can’t! You can get the slug or path of the page, but you simply can’t tell what the base URL is. You have to specify it.
This is a very basic concept, but if you are coming in fresh with years of WordPress experience, it can take some time for this info to sink in. You know that Gatsby is serverless and all but moments like this make you realize: There is no server.
Now that we have that sorted out, let’s jump to the actual methods for getting the URL of the current page.
Method 1: Use the href property of the window.location object
This first method is not specific to Gatsby and can be used in pretty much any JavaScript application in the browser. See, browser is the key word here.
Let’s say you are building one of those sharing components with an input field that must contain the URL of the current page. Here’s how you might do that:
import React from 'react';
const Foo = () => {
const url = typeof window !== 'undefined' ? window.location.href : '';
return (
<input type="text" readOnly="readonly" value={url} />
);
};
export default Foo;
If the window
object exists, we get the href
property of the location
object that is a child of the window
. If not, we give the url
variable an empty string value.
If we do it without the check and write it like this:
const url = window.location.href;
...the build will fail with an error that looks something like this:
failed Building static HTML for pages - 2.431s
ERROR #95312
"window" is not available during server-side rendering.
As I mentioned earlier, this happens because the browser doesn’t exist during the build time. That’s a huge disadvantage of this method. You can’t use it if you need the URL to be present on the static version of the page.
But there is a big advantage as well! You can access the window object from a component that is nested deep inside other components. In other words, you don’t have to drill the URL prop from parent components.
Method 2: Get the href property of location data from props
Every page and template component in Gatsby has a location prop that contains information about the current page. However, unlike window.location
, this prop is present on all pages.
Quoting Gatsby docs:
The great thing is you can expect the location prop to be available to you on every page.
But there may be a catch here. If you are new to Gatsby, you’ll log that prop to the console, and notice that it looks pretty much identical to the window.location
(but it’s not the same thing) and also contains the href
attribute. How is this possible? Well, it is not. The href
prop is only there during runtime.
The worst thing about this is that using location.href
directly without first checking if it exists won’t trigger an error during build time.
All this means that we can rely on the location
prop to be on every page, but can’t expect it to have the href
property during build time. Be aware of that, and don’t use this method for critical cases where you need the URL to be in the markup on the static version of the page.
So let’s rewrite the previous example using this method:
import React from 'react';
const Page = ({ location }) => {
const url = location.href ? location.href : '';
return (
<input type="text" readOnly="readonly" value={url} />
);
};
export default Page;
This has to be a top-level page or template component. You can’t just import it anywhere and expect it work. location
prop will be undefined.
As you can see, this method is pretty similar to the previous one. Use it for cases where the URL is needed only during runtime.
But what if you need to have a full URL in the markup of a static page? Let’s move on to the third method.
Method 3: Generate the current page URL with the pathname property from location data
As we discussed at the start of this post, if you need to include the full URL to the static pages, you have to specify the base URL for the website somewhere and somehow get it during build time. I’ll show you how to do that.
As an example, I’ll create a <link rel="canonical" href={url} />
tag in the header. It is important to have the full page URL in it before the page hits the browser. Otherwise, search engines and site scrapers will see the empty href
attribute, which is unacceptable.
Here’s the plan:
- Add the
siteURL
property tositeMetadata
ingatsby-config.js
. - Create a static query hook to retrieve
siteMetadata
in any component. - Use that hook to get
siteURL
. - Combine it with the path of the page and add it to the markup.
Let’s break each step down.
Add the siteURL property to siteMetadata in gatsby-config.js
Gatsby has a configuration file called gatsby-config.js
that can be used to store global information about the site inside siteMetadata
object. That works for us, so we’ll add siteURL
to that object:
module.exports = {
siteMetadata: {
title: 'Dmitry Mayorov',
description: 'Dmitry is a front-end developer who builds cool sites.',
author: '@dmtrmrv',
siteURL: 'https://dmtrmrv.com',
}
};
Create a static query hook to retrieve siteMetadata in any component
Next, we need a way to use siteMetadata
in our components. Luckily, Gatsby has a StaticQuery API that allows us to do just that. You can use the useStaticQuery
hook directly inside your components, but I prefer to create a separate file for each static query I use on the website. This makes the code easier to read.
To do that, create a file called use-site-metadata.js
inside a hooks
folder inside the src
folder of your site and copy and paste the following code to it.
import { useStaticQuery, graphql } from 'gatsby';
const useSiteMetadata = () => {
const { site } = useStaticQuery(
graphql`
query {
site {
siteMetadata {
title
description
author
siteURL
}
}
}
`,
);
return site.siteMetadata;
};
export default useSiteMetadata;
Make sure to check that all properties — like title
, description
, author
, and any other properties you have in the siteMetadata
object — appear in the GraphQL query.
Use that hook to get siteURL
Here’s the fun part: We get the site URL and use it inside the component.
import React from 'react';
import Helmet from 'react-helmet';
import useSiteMetadata from '../hooks/use-site-metadata';
const Page = ({ location }) => {
const { siteURL } = useSiteMetadata();
return (
<Helmet>
<link rel="canonical" href={`${siteURL}${location.pathname}`} />
</Helmet>
);
};
export default Page;
Let’s break it down.
On Line 3, we import the useSiteMetadata
hook we created into the component.
import useSiteMetadata from '../hooks/use-site-metadata';
Then, on Line 6, we destructure the data that comes from it, creating the siteURL
variable. Now we have the site URL that is available for us during build and runtime. Sweet!
const { siteURL } = useSiteMetadata();
Combine the site URL with the path of the page and add it to the markup
Now, remember the location prop from the second method? The great thing about it is that it contains the pathname
property during both build and runtime. See where it’s going? All we have to do is combine the two:
`${siteURL}${location.pathname}`
This is probably the most robust solution that will work in the browsers and during production builds. I personally use this method the most.
I’m using React Helmet in this example. If you haven’t heard of it, it’s a tool for rendering the head section in React applications. Darrell Hoffman wrote up a nice explanation of it here on CSS-Tricks.
Method 4: Generate the current page URL on the server side
What?! Did you just say server? Isn’t Gatsby a static site generator? Yes, I did say server. But it’s not a server in the traditional sense.
As we already know, Gatsby generates (i.e. server renders) static pages during build time. That’s where the name comes from. What’s great about that is that we can hook into that process using multiple APIs that Gatsby already provides.
The API that interests us the most is called onRenderBody
. Most of the time, it is used to inject custom scripts and styles to the page. But what’s exciting about this (and other server-side APIs) is that it has a pathname
parameter. This means we can generate the current page URL “on the server."
I wouldn’t personally use this method to add meta tags to the head section because the third method we looked at is more suitable for that. But for the sake of example, let me show you how you could add the canonical link to the site using onRenderBody
.
To use any server-side API, you need to write the code in a file called gatsby-ssr.js
that is located in the root folder of your site. To add the link to the head section, you would write something like this:
const React = require('react');
const config = require('./gatsby-config');
exports.onRenderBody = ({ pathname, setHeadComponents }) => {
setHeadComponents([
<link rel="canonical" href={`${config.siteMetadata.siteURL}${pathname}`} />,
]);
};
Let’s break this code bit by bit.
We require React on Line 1. It is necessary to make the JSX syntax work. Then, on Line 2, we pull data from the gatsby-config.js
file into a config
variable.
Next, we call the setHeadComponents
method inside onRenderBody
and pass it an array of components to add to the site header. In our case, it’s just one link tag. And for the href attribute of the link itself, we combine the siteURL
and the pathname
:
`${config.siteMetadata.siteURL}${pathname}`
Like I said earlier, this is probably not the go-to method for adding tags to the head section, but it is good to know that Gatsby has server-side APIs that make it possible to generate a URL for any given page during the server rendering stage.
If you want to learn more about server-side rendering with Gatsby, I encourage you to read their official documentation.
That’s it!
As you can see, getting the URL of the current page in Gatsby is not very complicated, especially once you understand the core concepts and know the tools that are available to use. If you know other methods, please let me know in the comments!
Resources
- JavaScript Window Interface
- Overview of the Gatsby Build Process
- Location Data from Props
- Gatsby Server Rendering APIs
- React Helmet
The post How to the Get Current Page URL in Gatsby appeared first on CSS-Tricks.
from CSS-Tricks https://ift.tt/2PKN1DH
via IFTTT
No comments:
Post a Comment