How to get page URL or hostname in NextJs Project?

Viewed 92326

I want to get the page's full URL or site hostname like the image below on Static Site Generator.

I will try with window.location.hostname, but it doesn't work.

The error: window not defined.

enter image description here

12 Answers

If you want the hostname inside getInitialProps on server side, still you can get it from req

Home.getInitialProps = async(context) => {
   const { req, query, res, asPath, pathname } = context;
   if (req) {
      let host = req.headers.host // will give you localhost:3000
     }
  }

With server-side rendering (getServerSideProps), you can use context.req.headers.host:

import type { GetServerSideProps, NextPage } from "next";

type Props = { host: string | null };

export const getServerSideProps: GetServerSideProps<Props> =
  async context => ({ props: { host: context.req.headers.host || null } });

const Page: NextPage<Props> = ({ host }) => <p>Welcome to {host || "unknown host"}!</p>;

export default Page;

But with static generation (getStaticProps), the hostname is not available, because there is no request to get it from. In general, a server doesn't know its own public hostname, so you need to tell it. Using Next.js environment variables, put this in .env.local:

HOST=example.com

Then access it with process.env['HOST']:

import type { GetStaticProps } from "next";

export const getStaticProps: GetStaticProps<Props> = 
  async context => ({ props: { host: process.env['HOST'] || null }});

The place where you are accessing the window make sure you add a check so that code is executed only on the browser and no during SSG"

if (typeof window !== 'undefined') {
   const hostname = window.location.hostname;
}

Update: If you have specified basePath in next.config.js:

module.exports = {
  basePath: 'https://www.example.com/docs',
}

Then using useRouter, you can access the base path:

import { useRouter } from 'next/router'

function Component() {
   const router = useRouter();
   
   console.log({ basePath: router.basePath}); 
   // { basePath: 'https://www.example.com/docs' }

   ...
}

But if you have a relative base path then you can use the first approach

Consider this package > next-absolute-url

import absoluteUrl from 'next-absolute-url'
const { origin } = absoluteUrl(req)
const apiURL = `${origin}/api/job.js`

If you deployed your Next.js app with now the apiURL will be something like https://your-app.now.sh/api/job.js.

However, if you are running the app locally the apiURL will be http://localhost:8000/api/job.js instead.

If you want to get the full URL:

import { useRouter } from 'next/router';
const { asPath } = useRouter();
    const origin =
        typeof window !== 'undefined' && window.location.origin
            ? window.location.origin
            : '';

    const URL = `${origin}${asPath}`;
    console.log(URL);

Using typeof window !== 'undefined' is the secure way. if (window) {} will run you into problems.

const hostname = typeof window !== 'undefined' && window.location.hostname ? window.location.hostname : '';
const origin = typeof window !== 'undefined' && window.location.origin ? window.location.origin : '';

Using above code will give you the frontend/outside hostname/origin the client using: example.com, www.example.com, www.example.com:80 and so on, not the localhost stuff. useRouter() will return the server side hostname/origin (localhost, localhost:3000)

I believe you're better of doing this with a combination of useRouter and useEffect hooks. In my case I wanted to dynamically set the og:url of my webpage. This is what I did. We have router.pathname as a dependency so that ogUrl is updated every time we move to a different page.

import { useRouter } from "next/router";
import { useState, useEffect } from "react";

const MyComponent = () => {

  const router = useRouter();
  const [ogUrl, setOgUrl] = useState("");


  useEffect(() => {
    const host = window.location.host;
    const baseUrl = `https://${host}`;

    setOgUrl(`${baseUrl}${router.pathname}`);
  }, [router.pathname]);


  return <div></div>
}

You need to ensure your access to window.location.hostname happens on the client-side only, and not during server-side rendering (where window does not exist). You can achieve that by moving it to a useEffect callback in your component.

function Component() {
    useEffect(() => {
        console.log(window.location.hostname) 
        console.log(window.location.href) // Logs `http://localhost:3000/blog/incididunt-ut-lobare-et-dolore`
    }, [])
    
    // Remaining code of the component
}

AFAIK there are two ways of doing this:

  1. Next JS provides us with the useRouter hook, first you have to import it in your component, then, to use the router object, you just have to declare it. For example:

    const router = useRouter();

    console.log(router.pathname);

    const {pathname} = router; <---- To access the pathname directly.

Besides this, as @Xairoo said before, if you want to use the window object, you have to check if window !== 'undefined' to avoid errors. The window not defined error happens because Next JS use NodeJS to render the app and the window object is not defined in Node JS.

You can find a more detailed explanation in this link.

req.headers are Symbols and not Objects, so to get value, you use the get method

const host = req.headers.get("host"); // stackoverflow.com

in Next.js you can do like this, by useEffect to get window.location.origin in client side, and set it to state.

work fine in : { "next": "12.1.6", "react": "18.1.0", }

const Home: NextPage = () => {
  const { asPath, query } = useRouter();

  const [loading, setLoading] = useState(false);
  const [loginCallBackURL, setLoginCallBackURL] = useState("");
 
  useEffect(() => { 
      setLoginCallBackURL(
        `${window.location.origin}/${query.redirect ? query.redirect : "user"}`,
      ); 
  }, []);

  // if you do something like this, it can't get loginCallBackURL
  // const loginCallBackURL = useMemo(() => {
  //   if (typeof window !== 'undefined') {
  //     return `${window.location.origin}/${
  //       query.redirect ? query.redirect : "user"
  //     }`;
  //   }
  //   return asPath;
  // }, [asPath, query]);
 
  return (
    <div>
      <Button
        variant="contained" 
        href={queryString.stringifyUrl({
          url: `${publicRuntimeConfig.API_HOST}/auth/google/login`,
          query: {
            callbackURL: loginCallBackURL,
          },
        })}
      >
        Sign in with google
      </Button>
    </div>
  );
};

export default Home;

none oh the answers above solved the problem and this is the solution i figured it out :

function return_url(context) {
  if (process.env.NODE_ENV === "production") {
    // if you are hosting a http website use http instead of https
    return `https://${context.req.rawHeaders[1]}`;
  } else if (process.env.NODE_ENV !== "production") {
    return "http://localhost:3000";
  }
}

and on the getServerSideProps or getStaticProps functions you use

export async function getServerSideProps(context) {
  let url = return_url(context);
  const data = await fetch(`${url}/yourEndPoint`).then((res) => res.json());
  return {
    props: {
      data: data,
    },
  };
}
Related