66

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

Raju Ahammad
  • 873
  • 2
  • 9
  • 15

16 Answers16

41

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
     }
  }
jameshfisher
  • 34,029
  • 31
  • 121
  • 167
Prabhu
  • 688
  • 5
  • 11
34

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 }});
jameshfisher
  • 34,029
  • 31
  • 121
  • 167
28

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);
Nirob Hasan
  • 413
  • 4
  • 3
  • that does not return the host name – ekkis Apr 15 '22 at 03:20
  • 1
    @ekkis `origin` gives you the hostname and `asPath` gives you the rest of the path. – Nirob Hasan May 16 '22 at 06:30
  • 2
    there is no host name for static-side rendering. there cannot be, because there isn't a request available – ekkis May 17 '22 at 20:10
  • 2
    in Next.js It will have Error: Hydration failed because the initial UI does not match what was rendered on the server. "next": "12.1.6", "react": "18.1.0", – Andy Ho May 29 '22 at 17:37
  • @AndyHo, does that happen even when the check for window has been done? – emanuel sanga Sep 11 '22 at 20:22
  • 1
    This is not a good solution. It doesn't support SSR and will generate hydration errors – Marc Mar 14 '23 at 15:45
  • `window` is sadly never a good solution in the React world. It only generates hydration errors and problems on the longer run, due to how it works. I'd advise against this and use the answer of @jameshfisher. You can take a look [here](https://stackoverflow.com/questions/55151041/window-is-not-defined-in-next-js-react-app) for more examples of `window` being a problem. – AirOne May 22 '23 at 12:39
15

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

Luke Taylor
  • 8,631
  • 8
  • 54
  • 92
Aadil Mehraj
  • 2,586
  • 1
  • 7
  • 17
11

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.

ekocibar
  • 149
  • 4
  • I think its for server-side requests in getInitialProps, but this is the answer that i looking for. thanks anyway – Necrofantaisie Feb 22 '21 at 03:56
  • @Necrofantaisie It looks like `next-absolute-url` also supports client side https://github.com/jakeburden/next-absolute-url/blob/master/index.js#L13 – Konstantin Komelin Oct 07 '21 at 07:15
  • req is undefined – Spencer Shattuck Oct 14 '22 at 16:00
  • Installing an unmaintained external dependency for such a small thing is not recommended, especially in enterprise production apps. Easy solutions directly implemented in your codebase is better than installing a package for this and that. – c4k Dec 23 '22 at 14:17
8

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)

Xairoo
  • 335
  • 1
  • 9
7

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>
}
chidimo
  • 2,684
  • 3
  • 32
  • 47
4

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
}
juliomalves
  • 42,130
  • 20
  • 150
  • 146
2

Here's how I solved it, and this also works with Next.js 13 App Router:

Create a hook, call it use-origin.jsx (or .tsx), and add it to the /hooks folder in the root:

'use client'; // this is Next 13 App Router stuff

import { useEffect, useState } from "react";

export default function useOrigin() {
  const [mounted, setMounted] = useState(false);
  const origin = typeof window !== 'undefined' && window.location.origin ? window.location.origin : '';

  useEffect(() => {
    setMounted(true)
  }, [])

  if (!mounted) {
    return null
  }

  return origin;
}

Now use this hook to access your dynamic BASE_URL route:

"use client";

import useOrigin from "@/hooks/use-origin"

export default function Test() {
  const origin = useOrigin();

  return (
    <div>{origin}</div>
  )
}
1

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

const host = req.headers.get("host"); // stackoverflow.com
Chukwuemeka Maduekwe
  • 6,687
  • 5
  • 44
  • 67
1

Using a middleware.js file that you add to the root of your project can give you access to the host name and provide a lot of flexibility to perform actions based on it if needed.

https://nextjs.org/docs/advanced-features/middleware

// Example: redirecting a domain to a subdomain

import { NextResponse } from "next/server";

// This function can be marked `async` if using `await` inside
export function middleware(request) {
  // Currently there is no main site so we redirect to the subdomain.
  const host = request.headers.get("Host");
  if (
    process.env.NODE_ENV === "production" &&
    host.startsWith("mydomain.com")
  ) {
    return NextResponse.redirect(new URL("https://mysubdomain.mydomain.com"));
  } else if (
    process.env.NODE_ENV === "staging" &&
    host.startsWith("staging.mydomain.com")
  ) {
    return NextResponse.redirect(
      new URL("https://mysubdomain-staging.mydomain.com")
    );
  }
}
blazeline
  • 339
  • 3
  • 8
0

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.

0

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,
    },
  };
}
gouder hicham
  • 123
  • 10
0

According to the answer mentioned here, you can use below code to get hostname in Next 13 using the new app directory (for server side components):

import { headers } from 'next/headers';

export default function Home() {
  const headersList = headers();
  
  headersList.get('host'); // to get domain
  headersList.get('next-url'); // to get url

  return <div>....</div>
}

NOTE: please note that Using it in a layout or page will opt a route into dynamic rendering at request time

Gangula
  • 5,193
  • 4
  • 30
  • 59
-1

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;

Andy Ho
  • 872
  • 7
  • 14
-2

We can get current url like this:

import { useRouter } from 'next/router';

const router = useRouter();
const origin = typeof window !== 'undefined' && window.location.origin ? window.location.origin : '';

const address_url = origin+router.asPath;    
Jay Nguyễn
  • 82
  • 1
  • 5