Add a component that dynamically swaps between next/link and a native anchor tag, and decides how to write the target attribute, all based on the href property.
I like when links to external pages open in a new tab, while internal pages stay in the same tab. It is especially beneficial to keep internal links using the next/link component, since it enables you to take advantage of the router and speed up subsequent page loads.
The first step is to add a custom link component that controls the rendering of the link. The basic logic would look something like this:
import NextLink from "next/link";
import { ExternalLink } from "path/to/external/link";
export const Link = ({ children, ...props }) => {
if (props.href.toString().startsWith("/")) {
return <NextLink {...props}>{children}</NextLink>;
} else {
return <ExternalLink child={children} href={props.href.toString()} />;
}
};
This logic is at the most basic level. If the href
property passed to the next/link
component begins with a slash (/
), consider it an internal link and pass it on to next/link
. Otherwise, render some external link component.
Now let’s add the external link component:
import { cloneElement, isValidElement } from "react";
export const ExternalLink = ({ child, href }) => {
if (!isValidElement(child)) {
throw new Error("Child must be a valid React element");
}
if (child.type !== "a") {
throw new Error("Child must be an <a> element");
}
return cloneElement(child, {
href,
target: "_blank",
rel: "noopener noreferrer",
});
};
This may look a little different to you, as it’s not immediately apparent that it’s rendering JSX code.
First, we do some runtime checking to ensure the child is a single <a>
component. After validation, we clone that element, adding the appropriate props (target
and rel
) in the process.
The beauty of this approach is that you don’t have to change anything with your code other than the import statements.
Wherever you have this in your code:
import Link from "next/link";
Change it to this:
import { Link } from "path/to/link/component";
Notice two things here:
Link
) changed to a named import ({ Link }
). While the React community still readily uses default imports, I like named imports for better clarity of what you’re importing. Here’s a take I generally align with.path/to/link/component
should be switched for wherever you put these components.That’s it! You should now have a working example of automatically opening external links in a new tab.
In the example below, I combined the components into a single component. Play around with it to make it work for you.
This was just a simple example and a starting point. Where you go from here is up to you.
But now you have this dynamic feature which is super powerful. You could do something like automatically added an icon to indicate that a link is going to open in a new window.
The possibilities are endless!