External link is not working in Next.js when you want to use Link component
Asked Answered
Y

7

30

I was very surprised that a simple Link component is not working in Next.js when you want to use an external URL and HTML Button tag inside it.

Below you can see how I tried to solve the problem:

Approach number 1:

<Link href="https://stackoverflow.com/">
  <button>StackOverflow</button>
</Link>

Approach number 2 (link without protocol):

<Link href="//stackoverflow.com/">
  <button>StackOverflow</button>
</Link>

Approach number 3 (link without protocol and with Link attribute prefetch set to false or even true):

<Link href="//stackoverflow.com/" prefetch={false}>
  <button>StackOverflow</button>
</Link>

IMPORTANT NOTE

Of course, mentioned case it's working when the URL is internal, like that:

<Link href="/stackoverflow">
  <button>StackOverflow</button>
</Link>

or when I will change HTML button tag into HTML A tag, like that:

<Link href="//stackoverflow.com/">
  <a>StackOverflow</a>
</Link>

In my case, I want to use the HTML button tag or any other UI component inside the Next.js Link component.

Yhvh answered 6/4, 2020 at 11:48 Comment(0)
Y
47

1. Solution for UI components inside Next.js Link component.

I have study Next.js documentation in more details and I found a very useful attribute to make an external link for any internal UI components (Semantic UI, Material UI, Reactstrap, etc.) inside Link component.

Let's take as an example a simple Semantic UI button component. To add an external link to the Next.js Link component we should use attribute passHref. This attribute is set to false by default. This attribute forces Link to send the href property to its child.

import { Button } from 'semantic-ui-react';
import Link from 'next/link';    

const Example = () => (
  <Link href="https://stackoverflow.com/" passHref={true}>
    <Button>StackOverflow</Button>
  </Link>
)

export default Example;

2. Solution for HTML elements (different that tag A)

Inside Next.js documentation you can find below sentences:

External URLs, and any links that don't require a route navigation using /pages, don't need to be handled with Link; use the anchor tag for such cases instead.

And I have to write that it is obvious, so in that case, if you need to use any other tag, for example, HTML button, you should use onClick event on it without Link component. The above code will look like this:

const clickHandle = () => {
  document.location.href = 'https://stackoverflow.com/';
}

const Example = () => (
  <button onClick={clickHandle}>StackOverflow</button>
)

export default Example;

UPDATE: Of course, I agree with devs who are writing that for external links we should not use the Link component. The best solution here is to use just pure HTML a tags or JS redirect solution on click event as it has been shown in point 2 (or any similar way). Worth to mention, that you can build your own component and based on the passed href attribute you can switch between Link component and HTML a tag, like that:

  // custom simple smart Link component
  import Link from 'next/link'; 
  
  const SmartLink = (link, url) => {
    const regEx = /^http/;

    return regEx.test(url) ? <Link href={url}>{link}</Link> : <a href={url}>{link}</a>;
  }
  
  export default SmartLink;

  // ways to call the component
  import SmartLink from 'path/to/SmartLink'; // set correct path

  // somewhere inside the render method
  // the below will use HTML A tag
  <SmartLink href="https://stackoverflow.com" link="external StackOverflow website" />
  // the below will use Next.js Link component
  <SmartLink href="/stackoverflow" link="internal StackOverflow page" />
Yhvh answered 6/4, 2020 at 13:35 Comment(10)
const URL = ({ link }) => { const { type, url } = link return ( <ListItem style={{ width: "inherit", display: "inline-block" }}> <a href={url}> <ListItemIcon> { { Github: <GitHub />, BitBucket: <InsertLink />, GitLab: <InsertLink />, StackOverflow: <InsertLink />, LinkedIn: <LinkedIn />, }[type] } </ListItemIcon> </a> </ListItem> ) } This one @Mario BossHalle
@Halle - you should use Link component from Next.js and wrapped your A tag inside it like that: <Link href="//stackoverflow.com/"> <a>StackOverflow</a> </Link>Yhvh
Cool, the most important thing is to put a // before to the URL.Halle
@Halle - I am glad that's working.. all the best..Yhvh
In my opinion, the accepted answer is wrong. passHref is used when the <a> tag is not a child of <Link>. For external URLs, simply use <a> without <Link>, see below.Mesentery
This solution ignores the fact that you should consider what type of element you're utilizing in the UI. For accessibility you should consider the intent and purpose for either a link or a button. For more information checkout a11y docs that discuss the differences.Sobriety
thank you for your answer it helped to solve problem in our projectJunko
I'm pulling the url from an API and no matter which method I use (a, MUI Link, onClick) the link still has the site base url added, which is not what's coming from the API. I'm surprised that this is an issue tbh!Paymaster
@Paymaster show us some code if you need help.. cheers..Yhvh
@MarioBoss thanks but I as usual, I figured it out as soon as I commented, it seems that // solution works so with MUI: <Link href={ //${item.url} } target="_blank" color="secondary" underline='none' > {item.url} </Link>Paymaster
L
19

The Link component is only for linking between pages within your Next app. Passing an external URL is not supported behaviour, and should give you an error that links to this page, which includes this section:

Why This Error Occurred

Next.js provides a router which can be utilized via a component imported via next/link, a wrapper withRouter(Component), and now a hook useRouter(). When using any of these, it is expected they are only used for internal navigation, i.e. navigating between pages in the same Next.js application.

Either you passed a non-internal href to a next/link component or you called Router#push or Router#replace with one.

Invalid hrefs include external sites (https://google.com) and mailto: links. In the past, usage of these invalid hrefs could have gone unnoticed but since they can cause unexpected behavior. We now show a warning in development for them.

If you render an <a> inside, the href gets passed on to that and works as expected using native browser behaviour, but other elements can't use that so you would have to handle that case yourself.

I'd suggest looking at what you're trying to achieve though -- what's wrong with using an <a> tag? It seems like the right tool for the job.

Lonnielonny answered 6/4, 2020 at 12:5 Comment(2)
Thanks for the answer but there are plenty of examples when you want to use a custom UI component as a link.. For example Button component from Semantic-UI..Yhvh
@MarioBoss there's a difference between something that looks like a button and a button component. An <a> should be used to link to a different URL (be that a different page, or an anchor on the current page), a <button> should be used to perform an action on the page (where the URL doesn't change). If you want an <a> to look like a button, style it as such. With semantic-ui you can <a href="…" class="ui button">I look like a button</a>.Melodics
M
16

In my opinion, the accepted answer is wrong. passHref is used when the <a> tag is not a child of <Link>. For external URLs, simply use <a> without <Link>, see below.

  const link = props.link.charAt(0) === '/' ? <Link as={stripUrlPlaceholder(props.link)} href="/">
    <a>{image}</a>
  </Link> : <a href={props.link} rel="noreferrer" target="_blank">{image}</a>;

  return <div className="banner">
    {link}
  </div>;
Mesentery answered 14/9, 2020 at 10:49 Comment(0)
J
8

Simply stated, use the <a> tag instead of the next <Link>. The next <Link> is for internal navigation.

Example 1.

   <a
     href='https://www.facebook.com/queueunderstop/'
     target={"_blank"}
     rel={"noreferrer"}>
       <Image
         className='gb'
         src='/images/icons/fb.png'
         alt='facebook'
         width={25}
         height={25}
       />
   </a>

This resolves the issue of the link opening a new link while simultaneously closing the main site. The main things to note are the attributes:

  1. target={"_blank"}
  2. rel={"noreferrer"}

I tried various combinations as well and landed on this after reading the documents carefully even though the documents do not say it implicitly. The documents cover more of does and not all the hypotheticals.

Juetta answered 29/12, 2021 at 23:52 Comment(0)
C
1

I had the same issue, somehow I tried the above answers, it wasn't really helpful. What I found is that if you add https or HTTP:// in, it will surely automatically allow you to open external web. Here for a sample:

<a href={`https://${Your link}`}> Open external Link </a>
Cobaltous answered 27/11, 2021 at 10:28 Comment(1)
But what if sometimes I have in "Your link" http, https, and sometimes not?Median
C
1

There is no need to use the next/link for external links, as it's only for client-side transitions between routes. Link is not intended for linking outside your app.

Cetus answered 27/11, 2021 at 22:46 Comment(0)
A
1

If you are getting the website URL from the API then you need to call the URL as below. In my case, I am getting the website URL like

{website : "nicobar.com"}

 <Link href={`https://${data?.website}`} target="_blank" passHref={true}>
       Visit Website
    </Link>
Acetabulum answered 23/2, 2023 at 12:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.